Ş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 const
referans 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::vector
almak, 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 for
dö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 var
bağı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, var
geçersiz bir durumda olmayı düşünmeliyiz .
Şimdi bunu auto&& var = foo();
, sorunuzda verildiği gibi, foo'nun bir T
by değeri döndürdüğü duruma uygulayalım . Bu durumda türünün türüne göre var
kesin 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 foo
geri 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_vector
dönecektir global_vec
bir rvalue ifade verir değeriyle. Alternatif olarak, çağırdığımızda foo<std::vector<int>&>
, referansla get_vector
geri dönerek global_vec
lvalue 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.