使用 std::tuple_element 时无效使用不完整类型的问题
Issue with invalid use of incomplete type when using std::tuple_element
以下代码为 std::tuple
实现了一个哈希函数,然后在 std::tuple
的 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.0
和 MSVC2015
中,我都收到以下错误(对于上面的每个违规行都是相同的):
error: invalid use of incomplete type 'class std::tuple_element<2ul, const std::tuple<int,int,int>&>
'
我不完全确定是什么导致了这个错误(虽然这可能是由于 "abstraction" 通过模板参数 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
并不专门用于引用,这意味着编译器会回退到主要的、未定义的 class 模板:
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
不过...
以下代码为 std::tuple
实现了一个哈希函数,然后在 std::tuple
的 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.0
和 MSVC2015
中,我都收到以下错误(对于上面的每个违规行都是相同的):
error: invalid use of incomplete type '
class std::tuple_element<2ul, const std::tuple<int,int,int>&>
'
我不完全确定是什么导致了这个错误(虽然这可能是由于 "abstraction" 通过模板参数 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
并不专门用于引用,这意味着编译器会回退到主要的、未定义的 class 模板:
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
不过...