【问题标题】:non-const reference of type from an rvalue来自右值的类型的非常量引用
【发布时间】:2014-06-06 13:22:23
【问题描述】:

考虑以下代码:

class Widget{};

template<typename T>
T &&foo2(T &&t){
    return std::forward<T>( t );
}

/// Return 1st element
template<typename T>
typename std::tuple_element<0, typename std::decay<T>::type >::type  &&foo(T &&t){
    return std::forward< typename std::tuple_element<0, typename std::decay<T>::type >::type >
            ( std::get<0>(t) );
}

Widget w;
auto list = std::make_tuple(
    w,
    Widget()
);


int main()
{
  auto &l  = foo(list );                      // This is NOT work
  //auto &l2 = foo2( std::get<0>(list) );     // This one works.
}

http://coliru.stacked-crooked.com/a/4d3b74ca6f043e45

当我试图编译这个时,我得到了以下错误:

error: invalid initialization of non-const reference of type 'Widget&' from an rvalue of type 'std::tuple_element<0ul, std::tuple<Widget, Widget> >::type {aka Widget}'

嗯,没关系,但是:

  • 起初,Widget w 不是临时的。为什么它把它当作临时的?

  • 其次,为什么 foo2 比 foo2 更有效?

附:如您所见,我尝试编写同时使用左值和右值操作的函数。如果第一个元素是临时的,我想返回右值,如果不是 - 左值。

【问题讨论】:

  • 你知道std::make_tuple(w, Widget())的类型是std::tuple&lt;Widget, Widget&gt;而不是std::tuple&lt;Widget&amp;, Widget&amp;&amp;&gt;吗?如果你想推导出一个引用类型的元组,你需要std::forward_as_tuple(w, Widget()),它将返回std::tuple&lt;Widget&amp;, Widget&amp;&amp;&gt;。或者这里真正的问题是你想要一个函数来创建一个元组,该元组具有一个用于左值引用输入的左值引用和一个用于右值引用输入的普通值?
  • 我需要一个同时接受 forward_as_tuple 和 make_tuple 的函数。并在第一种情况下返回右值引用,在第二种情况下返回左值引用(返回值是元组元素之一)。

标签: c++ templates c++11 rvalue-reference


【解决方案1】:

tuple_element 返回元素类型,而不是引用类型(除非元素类型本身是引用类型)。

如果类型 T 是引用类型,则需要让它返回引用类型。

这可以用条件表示:

typename std::conditional<std::is_lvalue_reference<T>::value,
    typename std::add_lvalue_reference<
        typename std::tuple_element<0, typename std::decay<T>::type >::type>::type,
    typename std::tuple_element<0, typename std::decay<T>::type >::type>::type

或者,更简单的是,使用decltype,因为std::get 已经为您执行了这个计算:

decltype(std::get<0>(std::declval<T &&>())) &&

【讨论】:

  • 我是否应该在使用decltype(std::get&lt;0&gt;(std::declval&lt;T &amp;&amp;&gt;())) &amp;&amp; 时将std::forward 添加到返回值?
【解决方案2】:

你可以这样做更简单:

template<typename T>
auto foo(T &&t) -> decltype(std::get<0>(std::forward<T>(t))) {
    return std::get<0>(t);
}

【讨论】:

  • 这将返回一个引用或值?
  • @tower120 参考。
  • 不应该是std::get&lt;0&gt;(std::forward&lt;T&gt;(t))吗?
  • 是的,我们不应该也转发std::get&lt;0&gt; 值吗?
  • @tower120 没必要,decltype 会根据左值/右值表达式推断出左值/右值引用类型。
【解决方案3】:

foo 返回一个右值引用,因此您不能将其绑定到 auto&amp;,因为这需要一个左值引用。

foo2 使用“通用引用”,在这种情况下评估为左值引用,因为 std::get 返回一个左值引用,并且您将其完美地转发到返回值。

【讨论】:

  • 我可以更改foo 使其也返回“通用参考”吗?
  • 通用引用不是一个实际的东西,它只是意味着我们的参数可以具有左值引用和右值引用类型,具体取决于模板实例化(这取决于调用站点提供的参数)。你想要的在 40two 的回答中,但是一定要转发参数t
猜你喜欢
  • 1970-01-01
  • 2020-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多