【问题标题】:Why passing rvalue reference (X&&) is AS IF passing lvalue reference (X&)?为什么传递右值引用 (X&&) 是 AS IF 传递左值引用 (X&)?
【发布时间】:2017-05-15 20:14:51
【问题描述】:

在研究右值引用时,我从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++ 规则强制执行此行为?

【问题讨论】:

  • "什么是强制执行这种奇怪行为的 C++ 规则" 你是什么意思?你的意思是哪个规则或规则集定义了这种行为?
  • @juanchopanza 我的意思是官方的语法规则。这就像 C++ 的法则。我不知道怎么称呼它。
  • 试试void foo(X&& x) { foo(std::move(x)); }
  • @Zereges 不行,这样毁了我脆弱的电脑实在是太慷慨了。
  • 您确实应该在问题中提供更多上下文。例如,您引用的问题可能会被删除,然后这个问题就不再有意义了。

标签: c++ c++11 language-lawyer rvalue-reference rvalue


【解决方案1】:

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

请注意,x 被声明为右值引用这一事实与 x 的值类别无关。

左值

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

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

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

【讨论】:

  • 只是好奇,从转换(X&& -> X&)来看,会有任何(可能很昂贵的)对象复制吗?也就是说,会调用拷贝构造函数还是拷贝赋值?
  • 没有。不涉及复制。
  • @javaLover 当调用foo#A?不,通过引用传递时不会发生复制;规则不会改变。
【解决方案2】:

虽然您已经回答了 C++ 标准在哪里指定了这一点,但让我也回答一下为什么它指定了这一点。

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

现在假设你有

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&amp;&amp;) 这将没有用,因为x 的内容在打印之前就已经消失了。

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

如果当g 的主体不再引用x 时,如果编译器能够以某种方式找出不再需要x,它可能会起作用,但这是一个非常复杂的问题为了解决问题,除非规则明确说明何时应该和不应该完成,否则编译器不会以相同的方式实现它,这使得在一个编译器上工作的代码很可能在下一个编译器上中断。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-23
    • 2014-04-04
    • 2016-10-22
    • 2013-05-31
    • 2016-01-06
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多