Rvalue引用和auto &&局部变量

Hai*_*Gad 8 c++ c++11

将局部变量定义为右值引用或转发(通用)引用有什么意义?据我所知,任何具有名称的变量都是左值,并将被视为向前移动.

例:

Widget&& w1 = getWidget();
auto&& w2 = getWidget();
Run Code Online (Sandbox Code Playgroud)

w1和w2都是左值,如果它们稍后作为参数传递,将被视为左值.他们的decltype可能不是,但这有什么区别?为什么有人需要这样定义变量?

Yak*_*ont 6

如果您有一个函数返回一个不能移动的临时对象。

Foo some_function();

auto&& f = some_function();
Run Code Online (Sandbox Code Playgroud)

这是合法的。 auto f = some_function();将要么复制(这可能很昂贵),要么无法编译(如果该类也无法复制)。

通常,auto&&根据其初始化内容推导出为r或左值引用,如果使用临时初始化,则可以延长其寿命,同时使您可以将其作为左值进行访问。

一个经典的用法是在“循环”模式中:

for( auto&& x : some_range )
Run Code Online (Sandbox Code Playgroud)

auto&& x = *it;生成的代码中实际上有一个。

您不能将非常量左值引用绑定到临时文件,因此您的另一选择是Widget const&,这不允许您在临时文件的生存期内对其进行修改。

该技术对于分解复杂的表达式并查看发生了什么也很有用。只要您不使用极其脆弱的表达式模板,就可以将表达式a+b+c*d转换为

auto&& c_times_d = d*d;
auto&& b_plus_c_times_d = b + decltype(c_times_d)c_times_d;
auto&& c_plus_b_plus_c_times_d = c + decltype(b_plus_c_times_d)b_plus_c_times_d;
Run Code Online (Sandbox Code Playgroud)

现在您可以访问其寿命已延长的临时对象,并且可以轻松地单步执行代码,或在复杂表达式中的步骤之间引入额外的步骤:这是机械发生的。

仅当您无法绑定每个子表达式时,才对脆弱的表达式模板表示担忧。(请注意,使用->可能会生成您可能不会注意到的大量子表达式。)

auto&&当我想说“我按原样存储某个函数的返回值,而不进行复制”时,我会用它,而表达式的类型并不重要。 auto当我想制作本地副本时。

在通用代码中,它可能非常有用。

Foo const& a(Foo*);
Bar a(Bar*);

template<class T>
auto do_stuff( T*ptr ) {
  auto&& x = a(ptr);
}
Run Code Online (Sandbox Code Playgroud)

在这里,如果您通过,Bar*它将存储临时项,但如果您将传递Foo*do_stuff它,则存储临时项const&

它做的最少。

这是一个函数返回不可移动的不可复制对象的示例,以及如何auto&&存储它的示例。否则它是无用的,但是它显示了它是如何工作的:

struct Foo {
  Foo(&&)=delete;
  Foo(int x) { std::cout << "Foo " << x << " constructed\n";
};
Foo test() {
  return {3};
}

int main() {
  auto&& f = test();
}
Run Code Online (Sandbox Code Playgroud)

  • @HaithamGad,因为它不会直接构造返回值。在这两种情况下,它都在逻辑上创建一个临时的“ Foo”,然后将其复制到返回值中(当然,移动)。该副本已被删除,但是*为了删除而必须合法*。同时,“ return {3};”只是直接构造返回值。 (3认同)
  • 有趣!如果将test()更改为return Foo(3);或return 3;为何会失败? (2认同)