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

时间:2016-08-31 17:26:52

标签: c++ c++14 stdtuple stdhash

The following code implements a hash function for a std::tuple which is then used in a different segment of my code-base in a std::unordered_map of std::tuples.

// 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);
    }
};

Then, just as an example, I would use this hash function-object like so,

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));

However, in both GCC 6.1.0 and MSVC2015, I receive the following errors (both the same for each offending line above):

error: invalid use of incomplete type 'class std::tuple_element<2ul, const std::tuple<int,int,int>&>'

I'm not entirely sure what's causing this error (though it may be due to "abstraction" of passing the std::tuple via the template parameter Tuple) or how it can be solved so any help is appreciated.

2 个答案:

答案 0 :(得分:3)

For a parameter declared as below:

const Tuple& t

decltype(t) yields:

const Tuple&

Similarly, for a parameter declared as:

const std::pair<Ty1, Ty2>& t

decltype(t) yields:

const std::pair<Ty1, Ty2>&

In both cases, the produced type is a reference to a tuple-like type. However, std::tuple_element is not specialized for references, which means, the compiler falls back to the primary, undefined class template:

template <size_t I, typename T> class tuple_element;

What you want, is a plain Tuple in the former case, and std::pair<Ty1, Ty2> in the latter case.

答案 1 :(得分:0)

如果您对类型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 ...