在我研究右值参考时,我发现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#B
拨打foo#A
,而不是foo#B
?
更具体地说,哪个C ++规则强制执行此行为?
答案 0 :(得分:7)
根据value categories的规则,作为命名参数,x
是lvalue。然后将调用foo#A
,因为x
可以绑定到左值引用,但不能绑定为rvalue-reference。
请注意,x
被声明为rvalue-reference的事实与x
的值类别无关。
左值
以下表达式是左值表达式:
- 范围内的变量或函数的名称,无论类型如何,例如std :: cin或std :: endl。即使变量的类型是rvalue引用,由其名称组成的表达式也是左值表达式;
您可以使用std::move
将其设为xvalue(右值),然后选择foo#B
(如您所料)。
答案 1 :(得分: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
,那么它可能会有效; sa 非常要解决的复杂问题,除非规则明确规定应该和不应该完成的时间,否则编译器不会以同样的方式实现它,因此很可能在一个编译器上运行的代码会在下一个编译器上中断。