通过使用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&&为示例,但是还没有解决这个问题。也许谁回答都能解释。