c++ - 为什么右值不能绑定(bind)到非 const 左值引用,除了写入临时值无效这一事实之外?

标签 c++ c++11 rvalue-reference rvalue lvalue

我已阅读 the SO question here并理解了这部分答案:“但是如果你将一个临时变量绑定(bind)到一个非常量引用,你可以“永远”传递它只是为了让你对对象的操作消失,因为在这个过程中的某个地方你完全忘记了这个是暂时的。”

也就是说,在下面:

#include <iostream>

void modifyValue(int& rValue) {
    rValue++;
}

int main() {
    modifyValue(9899);

    return 0;
}

如果右值可以绑定(bind)到非常量左值引用,那么可能会进行许多最终会被丢弃的修改(因为右值是临时的),这是无用的。

然而,这似乎定义明确(写入临时值就像写入任何值一样,生命周期与写入的有效性无关)。

这是禁止指定绑定(bind)的一个完全正确的理由(即使绑定(bind)会被明确定义),但是一旦我认为禁止这种绑定(bind)会强制需要转发引用,我的问题就开始形成了。

关于为什么右值不能绑定(bind)到非常量左值引用,是否还有任何其他原因(即除了写入临时值之外)?

最佳答案

简单的答案是,在大多数情况下,将临时变量传递给需要可变左值引用的函数表示逻辑错误,而 c++ 语言正在执行它最好能帮助您避免犯错误。

函数声明:void foo(Bar& b) 暗示了以下叙述:

foo takes a reference to a Bar, b, which it will modify. b is therefore both an input and an output

传递临时变量作为输出占位符通常比调用返回对象的函数更严重的逻辑错误,只是为了丢弃未经检查的对象。

例如:

Bar foo();

void test()
{
  /*auto x =*/ foo();  // probable logic error - discarding return value unexamined
}

但是,在这两个版本中,都没有问题:

void foo(Bar&& b)

foo takes ownership of the object referenced by Bar

void foo(Bar b)

foo conceptually takes a copy of a Bar, although in many cases the compiler will decide that creating and copying a Bar is un-necessary.

所以问题是,我们要达到什么目的?如果我们只需要一个 Bar 来工作,我们可以使用 Bar&& bBar b 版本。

如果我们想可能使用一个临时的并且可能使用一个现有的Bar,那么我们可能需要foo的两个重载>,因为它们在语义上会略有不同:

void foo(Bar& b);    // I will modify the object referenced by b

void foo(Bar&& b);   // I will *steal* the object referenced by b

void foo(Bar b);   // I will copy your Bar and use mine, thanks

如果我们需要这种可选性,我们可以通过将一个包裹在另一个中来创建它:

void foo(Bar& b)
{
  auto x = consult_some_value_in(b);
  auto y = from_some_other_source();
  modify_in_some_way(b, x * y);
}

void foo(Bar&& b)
{
  // at this point, the caller has lost interest in b, because he passed
  // an rvalue-reference. And you can't do that by accident.

  // rvalues always decay into lvalues when named
  // so here we're calling foo(Bar&)

  foo(b);   

  // b is about to be 'discarded' or destroyed, depending on what happened at the call site
  // so we should at least use it first
  std::cout << "the result is: " << b.to_string() << std::endl;
}

有了这些定义,这些现在都是合法的:

void test()
{
  Bar b;
  foo(b);              // call foo(Bar&)
  
  foo(Bar());          // call foo(Bar&&)

  foo(std::move(b));   // call foo(Bar&&)
  // at which point we know that since we moved b, we should only assign to it
  // or leave it alone.
}

好的,为什么这么关心?为什么在无意义的情况下修改临时文件会是逻辑错误?

好吧,想象一下:

Bar& foo(Bar& b)
{
  modify(b);
  return b;
}

我们期望做这样的事情:

extern void baz(Bar& b);

Bar b;
baz(foo(b));

现在想象这可以编译:

auto& br = foo(Bar());

baz(br); // BOOM! br is now a dangling reference. The Bar no longer exists

因为我们被迫在 foo 的特殊重载中正确处理临时文件,foo 的作者可以确信这个错误永远不会在您的代码中发生。

关于c++ - 为什么右值不能绑定(bind)到非 const 左值引用,除了写入临时值无效这一事实之外?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51836609/

相关文章:

c++ - C++ 中的除法汇总

c++ - 如何使用 __stdcall 来限定 C++ lambda?

c++ - 对象传递给 std::move 但未从中移出?

c++ - 构造函数使用可变数量的右值引用

c++ - 从函数返回重载运算符<<

c++ - 从另一个文件编译 __device__ 函数调用时出现 Visual Studio MSB3721 错误

c++ - 如何在 Windows 的 rootViewer 中设置 "Qt::FramelessWindowHint"标志来影响 DropArea

c++ - 如何创建过滤流的成员 vector ?

c++ - 移动构造函数应该采用 const 还是非 const 右值引用?

c++ - Qt 将 QAction 连接到带参数的函数