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

问题描述 投票:2回答:2

在我研究右值参考时,我发现了stackoverflow的一个奇怪的答案。

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

这是问题: -

void foo(X& x)  { /*complex thing*/       }      //#A
void foo(X&& x) { /*complex SAME thing*/  }      //#B

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

void foo(X& x)  {  /*complex thing*/ }      //#A
void foo(X&& x) { foo(x);            }      //#B

为什么我的版本不会导致堆栈溢出异常? 在我的版本中,为什么foo#Bfoo#A,而不是foo#B

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

c++ c++11 language-lawyer rvalue-reference rvalue
2个回答
7
投票

根据value categories的规则,作为一个命名参数,x是一个lvalue。然后将调用foo#A,因为x可以绑定到左值引用,但不是rvalue-reference。

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

lvalue

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

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

您可以使用std::move使其成为xvalue(rvalue),然后选择foo#B(如您所料)。


4
投票

虽然你已经有一个答案已经说明C ++标准指定了这一点,但我还要回答为什么它指定这个。

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

现在假设你有

void f(const X&x); // doesn't modify x
void f(X&&x); // clears x's data

你试试

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.