将局部变量定义为右值引用或转发(通用)引用有什么意义?据我所知,任何具有名称的变量都是左值,并将被视为向前移动.
例:
Widget&& w1 = getWidget();
auto&& w2 = getWidget();
Run Code Online (Sandbox Code Playgroud)
w1和w2都是左值,如果它们稍后作为参数传递,将被视为左值.他们的decltype可能不是,但这有什么区别?为什么有人需要这样定义变量?
如果您有一个函数返回一个不能移动的临时对象。
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)