【问题标题】:C++ universal references. Why rvalue reference becomes lvalue?C++ 通用参考。为什么右值引用变成左值?
【发布时间】:2019-03-17 18:01:36
【问题描述】:

这是困扰我的代码

‍‍‍‍‍#include <iostream>

#include "DataItem.h"


void testRef( const int & param )
{
    std::cout << "Lvalue reference" << std::endl;
}

void testRef( int && param )
{
    std::cout << "Rvalue reference" << std::endl;

    // Here's the thing I can't get. Why param is lvalue reference here??
    testRef( param );
}


template<class T>
void func( T && param )
{
    testRef( std::forward<T>( param ) );
}


int main2() 
{
    int a = 12;
    func( a );

    std::cout << "=================" << std::endl;

    func( 14 );

    std::cout << "=================" << std::endl;

    return 0;
}

当我在testRef( int &amp;&amp; param ) 中调用testRef() 时,我假设参数是右值引用,而不是右值函数将被调用(是的,永恒递归会发生)。但是调用了左值函数。为什么?

【问题讨论】:

  • 一旦有了名字,它就是一个左值。
  • 嗯!这是否意味着我应该在这种情况下使用std::move
  • 请从 cmets 移出您的答案。然后我可以将其标记为答案

标签: c++ rvalue type-deduction


【解决方案1】:

这样想,你在func中使用了std::forward&lt;T&gt;,所以同样地,为了确保参数作为右值引用转发,你必须在递归函数中做同样的事情:

void testRef(int && param)
{
    std::cout << "Rvalue reference" << std::endl;

    // Here's the thing I can't get. Why param is lvalue reference here??
    testRef( param );

    testRef(std::forward<int &&>(param)); // now it will stay an Rvalue reference
    testRef(std::move(param)); // make it an Rvalue reference
}

我们需要std::forwardstd::move 的原因是因为param 的类型是int&amp;&amp;,它是一个左值(即,当你使用右值引用参数时,它就是一个左值表达式)。

在幕后,这些模板最终将执行static_cast&lt;int &amp;&amp;&gt;,这会产生一个 xvalue 表达式(也被归类为右值表达式)。xvalue 表达式绑定到右值引用参数。

这可以通过查看Clang's syntax tree 来查看以下功能:

             rvalue reference parameter (which binds to rvalue expressions)
             vvvvvvvvvvv
void testRef(int&& param)
{
    //std::move(param);

                        lvalue expression of type int&&
                        vvvvv
    static_cast<int &&>(param);
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    xvalue expression 
    (considered an rvalue expression which binds to rvalue reference parameters) 
}

上述函数的抽象语法树:

TranslationUnitDecl
`-FunctionDecl <line:3:1, line:7:1> line:3:6 testRef 'void (int &&)'
  |-ParmVarDecl <col:14, col:21> col:21 used param 'int &&'
  `-CompoundStmt <line:4:1, line:7:1>
    `-CXXStaticCastExpr <line:6:5, col:30> 'int' xvalue static_cast<int &&> <NoOp>
      `-DeclRefExpr <col:25> 'int' lvalue ParmVar 0x55a692bb0a90 'param' 'int &&'

解释引用参数成为左值的一种简写方式是,当它有一个名称(id 表达式)时,它就是一个左值。

【讨论】:

  • 我认为当函数参数是通用引用时std::forward&lt;T&gt;是必要的。我说std::move 更适合这种情况吗?
  • std::move 清楚地表达了这种情况下的意图。您希望参数是右值引用。 std::forward 表达了您可能不知道它是什么但您希望它保持不变并匹配相关功能的意图。更好的解释here.
  • 正如@Sombrero Chicken 所说,如果我有参考名称,它就是左值。这意味着我们肯定知道paramvoid testRef(int &amp;&amp; param) 中如果是左值。这打算让我使用std::move。现在一切都清楚了,非常感谢
  • 只是为了明确一点:这不是应该如何使用std::forward。当您在非通用上下文中并且想要移动时,请使用std::move
猜你喜欢
  • 1970-01-01
  • 2018-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多