为什么传递右值引用(X &&)是AS IF传递左值引用(X&)?

jav*_*ver 2 c++ rvalue rvalue-reference language-lawyer c++11

当我研究右值参考时,我从stackoverflow中找到了一个奇怪的答案.

提问者希望避免在接收参数作为左值引用的函数与另一个接收右值引用的函数之间的代码重复.这两个函数都做同样的事情.

这是问题: -

void foo(X& x)  { /*complex thing*/       }      //#A
void foo(X&& x) { /*complex SAME thing*/  }      //#B
Run Code Online (Sandbox Code Playgroud)

这是建议的解决方案.它被我修改了一下: -

void foo(X& x)  {  /*complex thing*/ }      //#A
void foo(X&& x) { foo(x);            }      //#B
Run Code Online (Sandbox Code Playgroud)

为什么这不会导致堆栈溢出异常?
为什么foo#B打电话foo#A,但不是foo#B

更具体地说,哪个C++规则强制执行此行为?

son*_*yao 7

根据规则类别,作为命名参数,x左值.然后foo#A将被调用,因为x可以绑定到lvalue-reference,但不是rvalue-reference.

请注意,x声明为rvalue-reference 的事实与值类别无关x.

左值

以下表达式是左值表达式:

  • 范围内的变量或函数的名称,无论类型如何,例如std :: cin或std :: endl.即使变量的类型是右值引用,由其名称组成的表达式也是左值表达式;

您可以使用std::move它使其成为xvalue(rvalue),然后foo#B将被选中(如您所料).


小智 5

尽管您已经有了答案,说明 C++ 标准在何处指定了这一点,但让我也回答为什么它指定了这一点。

右值引用函数参数背后的想法是,函数可以假定引用的对象,或者至少其内容,在函数返回后将不再使用。

现在假设你有

void f(const X&x); // doesn't modify x
void f(X&&x); // clears x's data
Run Code Online (Sandbox Code Playgroud)

你试试

void g(X&&x) {
    f(x);
    std::cout << "we just called f(" << x << ")" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

如果它调用 ,这将没有用处f(X&&),因为x的内容在打印之前就会消失。

g需要明确告知f可以接管该对象。f不能假设g以后不再需要它。

如果编译器能够以某种方式弄清楚x不再需要它,当g's body 不再引用 时x,它可能会起作用,但这是一个需要解决的非常复杂的问题,除非规则明确说明何时应该这样做,何时不应该这样做,编译器不会以相同的方式实现它,这使得在一个编译器上运行的代码很可能在下一个编译器上崩溃。