Şunu kullanarak auto&& var = <initializer>şunları söylüyorsunuz: Bir lvalue veya rvalue ifadesi olup olmadığına bakılmaksızın herhangi bir başlatıcıyı kabul edeceğim ve sabitliğini koruyacağım . Bu genellikle yönlendirme için kullanılır (genellikle ile T&&). Bunun çalışmasının nedeni, "evrensel referans" auto&&veya herhangi bir şeyeT&& bağlanmasıdır .
Neden sadece bir kullanmak iyi diyebilirsiniz const auto&o çünkü aynı zamanda hiçbir şeye bağlamak? Bir constreferans kullanmayla ilgili problem, referans olması const! Daha sonra const olmayan referanslara bağlayamaz veya işaretlenmemiş üye işlevlerini çağıramazsınız const.
Örnek olarak, a std::vectoralmak, ilk öğesine bir yineleyici almak ve bu yineleyicinin işaret ettiği değeri bir şekilde değiştirmek istediğinizi düşünün :
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
Bu kod, başlatıcı ifadesinden bağımsız olarak iyi bir şekilde derlenecektir. auto&&Aşağıdaki şekillerde başarısız olmanın alternatifleri :
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
Bunun için auto&&mükemmel çalışıyor! Bunun auto&&gibi bir örnek, aralık tabanlı bir fordöngüdedir. Daha fazla ayrıntı için diğer soruma bakın.
Daha sonra referansta bunu bir lvalue veya bir rvalue olduğu gerçeğini korumak için kullanırsanız std::forward, auto&&kodunuz diyor: Şimdi nesnenizi bir lvalue veya rvalue ifadesinden aldığım için, orijinal olarak hangi değeri koruduğumu korumak istiyorum en verimli şekilde kullanabilirim - bu geçersiz kılabilir. De olduğu gibi:
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));
Bu use_it_elsewhere, orijinal başlatıcı değiştirilebilen bir değerken performans uğruna (kopyalardan kaçınarak) bağırsaklarını yırtmaya izin verir .
Bu, kaynakları çalabileceğimiz veya ne zaman çalabileceğimiz konusunda ne anlama geliyor var? Eh bu yana auto&&hiçbir şeye bağlanacak, muhtemelen söküp deneyemiyoruz varbağırsaklar kendimizi s - çok iyi, hatta const bir lvalue olabilir ya edilebilir. Bununla birlikte std::forward, içerilerini tamamen tahrip edebilecek diğer işlevlere de götürebiliriz . Bunu yapar yapmaz, vargeçersiz bir durumda olmayı düşünmeliyiz .
Şimdi bunu auto&& var = foo();, sorunuzda verildiği gibi, foo'nun bir Tby değeri döndürdüğü duruma uygulayalım . Bu durumda türünün türüne göre varkesin olarak kesinleştirileceğini biliyoruz T&&. Bunun bir değer olduğunu kesin olarak bildiğimiz için std::forward, kaynaklarını çalmak için izne ihtiyacımız yok . Bu özel durumda, değere göre foogeri döndüğünü bilerek , okuyucu bunu şu şekilde okumalıdır: Geri gönderilen geçici için bir rvalue referansı alıyorum foo, böylece mutlu bir şekilde hareket edebiliyorum.
Bir zeyilname olarak, some_expression_that_may_be_rvalue_or_lvalue"iyi bir kod değişebilir" durumu dışında, böyle bir ifade ne zaman ortaya çıkabilir söz etmeye değer olduğunu düşünüyorum . İşte burada tartışmalı bir örnek:
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;
}
Burada, get_vector<T>()genel türe bağlı olarak bir değer veya değer olabilen bu güzel ifade T. Biz esas olarak dönüş türünü get_vectorşablon parametresi aracılığıyla değiştiriyoruz foo.
Dediğimiz zaman foo<std::vector<int>>, get_vectordönecektir global_vecbir rvalue ifade verir değeriyle. Alternatif olarak, çağırdığımızda foo<std::vector<int>&>, referansla get_vectorgeri dönerek global_veclvalue ifadesine yol açar.
Eğer yaparsak:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
Beklendiği gibi aşağıdaki çıktıyı alıyoruz:
2
1
2
2
Değiştirmek olsaydı auto&&herhangi birine kodunda auto, auto&, const auto&veya const auto&&o zaman istediğimiz sonucu elde olmayacaktır.
auto&&Referansınızın bir değer veya rvalue ifadesi ile başlatılıp başlatılmadığına bağlı olarak program mantığını değiştirmenin alternatif bir yolu, tür özelliklerini kullanmaktır:
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&&? Ben döngü için bir aralık tabanlı nedenauto&&örnek olarak kullanmak için genişledi , ama bunun etrafında elde değil bakmayı düşünüyorum . Belki kim cevaplar açıklayabilir.