通过使用auto&& var = <initializer>
您的意思是:我将接受任何初始化程序,无论它是左值表达式还是右值表达式,我都将保留其constness。通常用于转发(通常使用T&&
)。之所以起作用,是因为“通用引用” auto&&
或T&&
将绑定到任何东西。
你可能会说,好吧,为什么不直接使用const auto&
,因为这将也绑定到什么?使用const
引用的问题在于const
!您以后将无法将其绑定到任何非const引用或调用未标记的任何成员函数const
。
例如,假设您要获取一个std::vector
,将迭代器带到其第一个元素,然后以某种方式修改该迭代器指向的值:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
无论初始化表达式如何,此代码都可以正常编译。auto&&
失败的替代方式如下:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
因此,这auto&&
很完美!auto&&
在基于范围的for
循环中使用此类示例。有关更多详细信息,请参见我的其他问题。
如果再使用std::forward
您auto&&
参考,以保持一个事实,即它本来无论是一个左或右值,你的代码说:现在,我已经得到了你的对象从任何一个左或右值的表情,我想保留原来为准它valueness因此,我可以最有效地使用它-这可能会使它失效。如:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
use_it_elsewhere
当原始的初始值设定项是可修改的右值时,这可以消除其胆量以提高性能(避免复制)。
对于我们是否可以或何时可以从中窃取资源,这意味着什么var
?好吧,因为auto&&
遗嘱会绑定到任何东西,所以我们可能无法尝试摆脱var
自身的烦恼-它很可能是左值甚至是const。但是std::forward
,我们可以将其用于可能完全破坏其内部的其他功能。一旦这样做,我们应该考虑var
处于无效状态。
现在,将其应用于auto&& var = foo();
您的问题中给出的的情况,其中foo返回一个T
按值。在这种情况下,我们肯定知道的类型var
将推导为T&&
。由于我们肯定知道它是右值,因此不需要std::forward
的许可即可窃取其资源。在这种特定情况下,知道foo
按值返回,读者应该将其读取为:我对从返回的临时值进行了右值引用foo
,因此我可以很高兴地从中移出。
作为附录,我认为值得一提的是何时出现类似“表达式some_expression_that_may_be_rvalue_or_lvalue
可能会更改”的情况。所以这是一个人为的例子:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
这get_vector<T>()
是一个可爱的表达式,根据泛型类型,它可以是左值或右值T
。我们实质上get_vector
通过的模板参数更改的返回类型foo
。
当我们调用时foo<std::vector<int>>
,get_vector
将按global_vec
值返回,从而给出一个右值表达式。或者,当我们调用时foo<std::vector<int>&>
,get_vector
将global_vec
通过引用返回,从而产生一个左值表达式。
如果这样做:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
正如预期的那样,我们得到以下输出:
2
1
2
2
如果你要改变auto&&
在代码中任何的auto
,auto&
,const auto&
,或const auto&&
那么我们不会得到我们想要的结果。
根据auto&&
引用是用左值表达式还是右值表达式初始化的,更改程序逻辑的另一种方法是使用类型特征:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
auto&&
?我一直在考虑为什么将基于范围的for循环扩展auto&&
为示例,但是还没有解决这个问题。也许谁回答都能解释。