【问题标题】:std::make_tuple doesn't make referencesstd::make_tuple 不做参考
【发布时间】:2011-12-13 14:40:30
【问题描述】:

我一直在尝试将std::tuple 与参考文献结合使用:

#include <iostream>
#include <tuple>

int main() {
  int a,b;
  std::tuple<int&,int&> test(a,b);
  std::get<0>(test) = 1;
  std::get<1>(test) = 2;
  std::cout << a << ":" << b << std::endl;

  // doesn't make ref, not expected
  auto test2 = std::make_tuple(a,b);
  std::get<0>(test2) = -1;
  std::get<1>(test2) = -2;
  std::cout << a << ":" << b << std::endl;

  int &ar=a;
  int &br=b;
  // why does this not make a tuple of int& references? can we force it to notice?
  auto test3 = std::make_tuple(ar,br);
  std::get<0>(test3) = -1;
  std::get<1>(test3) = -2;
  std::cout << a << ":" << b << std::endl;
}

在此处的三个示例中,前两个按预期工作。然而,第三个没有。我期待auto 类型(test3)与test 类型(即std::tuple&lt;int&amp;,int&amp;&gt;)相同。

std::make_tuple 似乎无法自动生成引用元组。为什么不?除了自己明确地构建这种类型的东西之外,我还能做些什么来做到这一点?

(编译器为 g++ 4.4.5,using 4.5 doesn't change it

【问题讨论】:

    标签: c++ reference tuples c++11


    【解决方案1】:

    试试forward_as_tuple:

    auto test3 = std::forward_as_tuple(ar,br);
    

    【讨论】:

    • @Dani:为什么不能存储tuple&lt;int&amp;, int&amp;&gt;(上面使用的std::forward_as_tuple的返回类型)? Fwiw,我在发布之前测试了代码。
    • 它可以在 g++ 4.7.0(10/10/2011 快照)和 4.6.1 上按预期编译和工作,但在我尝试过的任何 4.5 或更早版本中都没有
    • 您可以自己编写用于 4.5 及更早版本(只要您有右值引用和可变参数支持)。这是一个微不足道的辅助函数。它以T&amp;&amp;... 作为参数并返回tuple&lt;T&amp;&amp;...&gt;(std::forward&lt;T&gt;(t)...)
    【解决方案2】:

    std::make_tuple&lt;int&amp;, int&amp;&gt;(a, b);

    诚然,这有点违背目的,但对于像 make_shared 这样的功能,您仍然可以获得好处。

    警告,我没有尝试编译这个,但我相信它会工作。

    【讨论】:

    • 我们的想法是避免明确指定类型,因为在实际代码中执行起来要麻烦得多
    • @awoodland 是的,我的回答有点糟糕。但我之前不知道 std::forward_as_tuple 。这对我来说似乎是正确的答案。
    • make_tuple() 存在以推断类型。如果您可以/想要明确指定它们,那么使用make_tuple 将毫无意义,您应该直接调用构造函数。
    【解决方案3】:

    怎么样:

    auto test3 = std::make_tuple(std::ref(a),std::ref(b));
    

    【讨论】:

    • std::ref 来自哪里?使用 g++ 4.7,我得到“ref 不是 std 的成员”,并且 grep 试图解决这个问题非常无益。
    • 应该在&lt;functional&gt;。在 MSVC2010 上,它似乎被 &lt;tuple&gt; 拖进来了
    • 我喜欢这个答案,因为我认为它使std::make_tuple(p1?std::ref(a):std::cref(a), p2?std::ref(b):std::cref(b)) 之类的东西变得可行
    【解决方案4】:

    std::tie 进行非const 引用。

    auto ref_tuple = std::tie(a,b); // decltype(ref_tuple) == std::tuple<int&, int&>
    

    对于 const 引用,您可能需要 std::cref 包装函数:

    auto cref_tuple = std::make_tuple(std::cref(a), std::cref(b));
    

    或者在将变量传递给 std::tie 之前使用简单的 as_const 助手来限定变量:

    template<class T>
    T const& as_const(T& v){ return v; }
    
    auto cref_tuple = std::tie(as_const(a), as_const(b));
    

    或者,如果你想变得花哨,写你自己的ctie(重复使用std::tieas_const):

    template<class... Ts>
    std::tuple<Ts const&...> ctie(Ts&... vs){
      return std::tie(as_const(vs)...);
    }
    
    auto cref_tuple = ctie(a, b);
    

    【讨论】:

    • 值得强调的是,std::[c]ref() 上的 tuple 参数可以与缺少它的参数混合,以推导出值和引用参数的混合。另外:C++17 可以从构造函数表达式中推导出模板参数,因此可以使用std::tuple{...} 而不是std::make_tuple(...),并且它添加了一个官方的std::as_const()。无论如何,关于std::tie() 的好点;我倾向于忘记它的存在,而且它的噪音远低于std::forward_as_tuple()
    • "std::tie 使非const 引用。"这种说法有些误导。 std::tie 创建一个对其参数的左值引用元组。这些左值引用是否引用const 限定类型(又名const 左值引用)取决于推导的类型。如果ab 被声明为const,那么std::tie 将产生const 左值引用。
    【解决方案5】:

    原因:make_tuple 参数通过 const 引用 (const T&amp;) 传递,因此如果您传递 int&amp;T 匹配 int。如果它推导出Tint&amp;,则参数将为const T&amp;&amp;,您将得到一个编译错误。

    【讨论】:

    • 引用折叠规则是T = int&amp;应用于const T&amp;导致const int&amp;
    【解决方案6】:

    在 C++14 中,你可以这样进行:

    template<typename ...T, size_t... I>
    auto make_rtuple_helper(std::tuple<T...>& t ,  std::index_sequence<I...>)
    -> std::tuple<T&...>
    { return std::tie(std::get<I>(t)...) ;}
    
    template<typename ...T>
    std::tuple<T&...> make_rtuple( std::tuple<T...>& t )
    {
        return make_rtuple_helper( t, std::make_index_sequence<sizeof...(T)>{});
    }
    

    查看它在 coliru 中的工作原理:http://coliru.stacked-crooked.com/a/a665130e17fd8bcc

    干杯 A.A.

    【讨论】:

      猜你喜欢
      • 2017-04-16
      • 2016-08-18
      • 2016-03-14
      • 2021-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 2012-10-25
      相关资源
      最近更新 更多