Rvalue参考和auto&&局部变量

时间:2016-03-21 14:32:28

标签: c++ c++11

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

示例:

Widget&& w1 = getWidget();
auto&& w2 = getWidget();

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

4 个答案:

答案 0 :(得分:5)

如果你有一个函数返回一个无法移动的临时函数。

Foo some_function();

auto&& f = some_function();

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

通常,auto&&推导出r或左值引用,具体取决于它初始化的内容,如果使用临时值初始化,则延长其生命周期,同时允许您将其作为左值访问。

经典用法是'just-loop'模式:

for( auto&& x : some_range )

生成的代码中实际存在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;

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

如果您无法绑定每个子表达式,则只关注脆弱的表达式模板。 (请注意,使用->可能会生成大量您可能不会注意到的子表达式。)

当我想说“我正在存储某个函数的返回值,而不是复制”时,我使用auto&&,并且表达式的类型并不重要。 auto是我想制作本地副本的时候。

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

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

template<class T>
auto do_stuff( T*ptr ) {
  auto&& x = a(ptr);
}

此处如果您传递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();
}

答案 1 :(得分:1)

一个例子可能是数组

template<class T, int N> using raw_array = T[N];

然后

auto && nums = raw_array<int,4>{101, 102, 103, 104};

这允许临时用作普通数组。

答案 2 :(得分:0)

据我所知,没有真正的,也就是广泛使用的目的来定义本地右值引用,因为它们的性质,不绑定到左值,只对重载和演绎有帮助,所以将它们定义为参数功能。

可以使用它们,将它们绑定到临时值,例如 int &&rref = 5*2; 但由于几乎所有编译器都在优化表达式 int i = 5*2; 没有实际需要,性能不佳或避免复制。

答案 3 :(得分:0)

声明为auto&&的变量将遵循完美的转发规则。亲自试试吧。这甚至是在c ++ 14中使用lambdas进行完美转发的方式。

const Type& fun1();
Type&& fun2();

auto&& t1 = fun1(); // works
auto&& t2 = fun2(); // works too