为什么传递右值引用 (X&&) 就像传递左值引用 (X&)?
Why passing rvalue reference (X&&) is AS IF passing lvalue reference (X&)?
在研究右值引用的过程中,我发现了Whosebug的一个奇怪的答案。
发问者希望避免一个接收参数作为 左值引用 的函数与另一个接收 右值引用的函数之间的代码重复。两个函数做同样的事情。
这里是问题所在:-
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++ 规则强制执行此行为?
根据value categories, as a named parameter, x
is an lvalue的规则。然后 foo#A
将被调用,因为 x
可以绑定到左值引用,而不是右值引用。
请注意,x
被声明为右值引用这一事实与 x
的值类别无关。
lvalue
The following expressions are lvalue expressions:
- the name of a variable or a function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
您可以使用 std::move
使其成为 xvalue(右值),然后 foo#B
将被选中(如您所料)。
虽然您已经有了一个答案,说明 C++ 标准在哪里指定了这一点,但让我也回答一下 为什么 它指定了这一点。
右值引用函数参数背后的想法是函数可以假定引用的对象,或者至少它的内容,在函数 returns.
之后将不再被使用。
现在假设你有
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
以后不再需要它。
当 g
的正文不再引用 x
时,如果编译器能够以某种方式找出不再需要 x
,它可能会起作用,但那是 非常 需要解决的复杂问题,除非规则明确说明何时应该和不应该完成,编译器不会以相同的方式实现它,这使得代码很可能适用于一个编译器在下一个中断。
在研究右值引用的过程中,我发现了Whosebug的一个奇怪的答案。
发问者希望避免一个接收参数作为 左值引用 的函数与另一个接收 右值引用的函数之间的代码重复。两个函数做同样的事情。
这里是问题所在:-
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++ 规则强制执行此行为?
根据value categories, as a named parameter, x
is an lvalue的规则。然后 foo#A
将被调用,因为 x
可以绑定到左值引用,而不是右值引用。
请注意,x
被声明为右值引用这一事实与 x
的值类别无关。
lvalue
The following expressions are lvalue expressions:
- the name of a variable or a function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
您可以使用 std::move
使其成为 xvalue(右值),然后 foo#B
将被选中(如您所料)。
虽然您已经有了一个答案,说明 C++ 标准在哪里指定了这一点,但让我也回答一下 为什么 它指定了这一点。
右值引用函数参数背后的想法是函数可以假定引用的对象,或者至少它的内容,在函数 returns.
之后将不再被使用。现在假设你有
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
以后不再需要它。
当 g
的正文不再引用 x
时,如果编译器能够以某种方式找出不再需要 x
,它可能会起作用,但那是 非常 需要解决的复杂问题,除非规则明确说明何时应该和不应该完成,编译器不会以相同的方式实现它,这使得代码很可能适用于一个编译器在下一个中断。