将局部变量定义为右值引用或转发(通用)引用有什么意义?据我所知,任何具有名称的变量都是左值,并将被视为向前推进。
示例:
Widget&& w1 = getWidget();
auto&& w2 = getWidget();
w1和w2都是左值,如果它们稍后作为参数传递,将被视为左值。他们的decltype可能不是,但这有什么区别?为什么有人需要这样定义变量?
答案 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