使用std::tuple_element时无效使用不完整类型的问题

Issue with invalid use of incomplete type when using std::tuple_element

本文关键字:用不完 类型 问题 tuple std element 使用 无效      更新时间:2023-10-16

下面的代码实现了std::tuple的哈希函数,然后在std::tuple s的std::unordered_map的代码库的不同段中使用。

// compute hash function recursively through each std::tuple element
template<class Tuple, std::size_t N>
struct tuple_hash_compute {
    static std::size_t hash_compute(const Tuple& t) {
        using type = typename std::tuple_element<N-1, decltype(t)>::type; // OFFENDING LINE
        return tuple_hash_compute<Tuple, N-1>::hash_compute(t)
            + std::hash<type>()(std::get<N-1>(t));
    }
};
// base helper
template<class Tuple>
struct tuple_hash_compute<Tuple, 1> {
    static std::size_t hash_compute(const Tuple& t) {
        using type = typename std::tuple_element<0, decltype(t)>::type; // OFFENDING LINE
        return 51U + std::hash<type>()(std::get<0>(t))*51U;
    }
};
// tuple_hash function object
struct tuple_hash {
    template<class... Args>
    std::size_t operator()(const std::tuple<Args...>& t) const {
        return tuple_hash_compute<decltype(t), sizeof...(Args)>::hash_compute(t);
    } 
    // will use std::unordered_map of std::pair too, so overload reqd
    template<class Ty1, class Ty2>
    std::size_t operator()(const std::pair<Ty1, Ty2>& p) const {
        return tuple_hash_compute<decltype(t), 2>::hash_compute(p);
    }
};

然后,作为一个例子,我会像这样使用这个哈希函数对象,

std::unordered_map<std::tuple<int,int,int>, std::size_t, tuple_hash> agg_map;
agg_map.insert(std::make_pair(std::make_tuple(1,2,3), 0U));
agg_map.insert(std::make_pair(std::make_tuple(4,5,6), 1U));

然而,在GCC 6.1.0MSVC2015中,我收到以下错误(对于上面的每个违规行都是相同的):

错误:无效使用不完整类型' class std::tuple_element<2ul, const std::tuple<int,int,int>&> '

我不完全确定是什么原因导致这个错误(虽然它可能是由于通过模板参数Tuple传递std::tuple的"抽象")或如何解决它,所以任何帮助是感激的。

对于如下声明的参数:

const Tuple& t

decltype(t)产量:

const Tuple&
类似地,对于声明为: 的形参:
const std::pair<Ty1, Ty2>& t

decltype(t)产率:

const std::pair<Ty1, Ty2>&

在这两种情况下,生成的类型都是对类元组类型的引用。然而,std::tuple_element不是专门用于引用的,这意味着编译器会退回到主的、未定义的类模板:

template <size_t I, typename T> class tuple_element;

您想要的是,在前一种情况下是普通的Tuple,在后一种情况下是std::pair<Ty1, Ty2>

如果类型Tuple可能是引用也可能不是引用,则可以像这样使用std::remove_reference:

typename std::tuple_element<num, typename std::remove_reference<Tuple>::type>::type

,对于c++ 17,

std::tuple_element_t<num, std::remove_reference_t<Tuple>>

PS:我不认为它适用于std::reference_wrapper虽然…