C++ std::tuple完美转发失效:左值tuple调用索引查找函数编译失败
问题根因
问题出在你对通用引用(转发引用)推导规则的处理不到位:
- 当你把左值
t传入tupleIndexOf的Tuple&&参数时,根据C++引用折叠规则,模板参数Tuple会被推导为左值引用类型TP&,而不是纯tuple类型TP。 - 你后续直接把带引用的
Tuple类型传给了std::tuple_size、std::tuple_element这两个元工具,这两个工具都不支持接收带引用修饰的tuple类型,直接触发编译错误。 - 额外还有一处类型匹配问题:你显式指定了
equal<Tuple,I,V>的模板参数,此时如果传入的V是左值(比如main里的str),V会被推导为std::string&,和std::tuple_element返回的元素类型std::string不相等,会导致enable_if匹配失败。
修复代码
你只需要对所有参与元计算的类型提前去掉引用修饰即可,修改后完整可运行代码如下:
#include <type_traits> #include <iostream> #include <tuple> #include <string> template<int ...INDEX> struct Sequence { }; template<int N, int ...MORE> struct MkSequence { using type = typename MkSequence<N-1, N-1, MORE...>::type; }; template<int ... LEFT> struct MkSequence<0, LEFT...> { using type = Sequence<LEFT...>; }; // 修改equal的enable_if判断,给Tuple、V都去掉引用 template<typename Tuple, int I, typename V> bool equal(Tuple &&t, V&& v, typename std::enable_if<std::is_same< typename std::tuple_element<I, typename std::remove_reference_t<Tuple>>::type, typename std::remove_reference_t<V> >::value >::type* = nullptr) { return std::get<I>(std::forward<Tuple>(t)) == std::forward<V>(v); }; template<typename Tuple, int I, typename V> bool equal(Tuple &&t, V&& v, typename std::enable_if<!std::is_same< typename std::tuple_element<I, typename std::remove_reference_t<Tuple>>::type, typename std::remove_reference_t<V> >::value>::type* = nullptr) { return false; } template<typename Tuple, typename V, int ...I> int tupleIndexOfWithSeq(Tuple &&t, V&& v, Sequence<I...>) { // equal只显式传索引I,剩下的模板参数让编译器自动推导,避免类型匹配问题 bool list[] = {(equal<I>(std::forward<Tuple>(t),std::forward<V>(v)))...}; int i = 0; for(auto val: list) { if(val) return i; i++; } return -1; }; template<typename Tuple, typename V> int tupleIndexOf(Tuple &&t, V&& v) { // 给Tuple去掉引用后再传给std::tuple_size using DecayedTuple = std::remove_reference_t<Tuple>; return tupleIndexOfWithSeq(std::forward<Tuple>(t), std::forward<V>(v), typename MkSequence<std::tuple_size<DecayedTuple>::value>::type{}); }; int main() { using TP = std::tuple<int, std::string>; TP t(1, "hello"); std::string str="hello"; std::cout<<tupleIndexOf(TP(1, "hello"), str)<<std::endl; // 现在可以正常编译运行 std::cout<<tupleIndexOf(t, str)<<std::endl; return 0; }
内容的提问来源于stack exchange,提问作者qinghan dai
相关产品推荐
相关产品推荐

