【问题标题】:C++ perfect forward on std::tupleC++ 在 std::tuple 上完美向前
【发布时间】:2021-12-28 01:25:38
【问题描述】:

我起草了代码来找出元组元素中类型为 V 的值的索引。 一般来说,代码可以工作,但元组的完美前锋似乎不起作用。 请参考 main 函数中的最后两行注释。

#include <type_traits>
#include <iostream>
#include <tuple>
#include <cxxabi.h>
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...>;
};

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,Tuple>::type, 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,Tuple>::type, V>::value>::type* = nullptr)
{
  return false;
}

template<typename Tuple, typename V, int ...I>
int tupleIndexOfWithSeq(Tuple &&t, V&& v, Sequence<I...>)
{
  bool list[] = {(equal<Tuple,I,V>(std::forward<Tuple>(t),std::forward<V>(v)))...};
  int i = 0;
  for(auto v: list)
  {
    if(v) return i;
    i++;
  }
  return -1;
};

template<typename Tuple, typename V>
int tupleIndexOf(Tuple &&t, V&& v)
{
  return tupleIndexOfWithSeq(std::forward<Tuple>(t), std::forward<V>(v), typename MkSequence<std::tuple_size<Tuple>::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;
  //Why the following calling of tupleIndexOf does not compile:
  //std::cout<<tupleIndexOf(t, str)<<std::endl;
};

【问题讨论】:

    标签: c++ c++11 templates tuples forwarding-reference


    【解决方案1】:

    这就是forwarding reference 的工作原理,当被传递像tupleIndexOf(t, str) 这样的左值时,模板参数Tuple 将被推断为std::tuple&lt;int, std::string&gt; &amp; 的左值引用,这使得std::tuple_size&lt;Tuple&gt;::value 停止工作,因为std::tuple_size需要 std::tuple 类型,但不是对 std::tuple 的引用。 (当被传递像tupleIndexOf(TP(1, "hello"), str)这样的右值时,Tuple将被推导出为std::tuple&lt;int, std::string&gt;,那么它工作得很好。)

    您可以在模板参数Tuple 上使用std::remove_reference(也可以在两个equal 重载中使用std::tuple_element),例如

    ...
    
    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<Tuple>::type>::type,
    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                 typename std::remove_reference<V>::type>::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<Tuple>::type>::type, 
    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                 typename std::remove_reference<V>::type>::value>::type* = nullptr)
    {
      return false;
    }
    
    ...
    
    template<typename Tuple, typename V>
    int tupleIndexOf(Tuple &&t, V&& v)
    {
      return tupleIndexOfWithSeq(std::forward<Tuple>(t), 
                                 std::forward<V>(v), 
                                 typename MkSequence<std::tuple_size<
                                   typename std::remove_reference<Tuple>::type>::value>
    //                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                 ::type{});
    }
    

    LIVE

    【讨论】:

    • 感谢您的回答。答案确实触及了重点。要使两个“相等”正常工作,还需要将 remove_reference 应用于“V”类型,否则 std::is_same 会将“int”和“int&”视为两种不同的类型。
    猜你喜欢
    • 2015-04-11
    • 1970-01-01
    • 2012-01-06
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 2019-03-20
    相关资源
    最近更新 更多