Bir rvalue referansı beyan eder (standart teklif dokümanı).
İşte rvalue referanslarına bir giriş .
İşte Microsoft'un standart kitaplık geliştiricilerinden birinin değer referanslarına muhteşem bir bakış .
DİKKAT: MSDN'deki bağlantılı makale ("Rvalue Referansları: VC10, Bölüm 2'deki C ++ 0x Özellikleri) Rvalue referanslarına çok açık bir giriştir, ancak bir zamanlar taslak C ++ 11'de geçerli olan Rvalue referansları hakkında açıklamalar yapar standart, ancak sonuncusu için doğru değil! Özellikle, çeşitli noktalarda değer referanslarının bir zamanlar doğru olan ancak değiştirilen değerlere bağlanabileceğini söylüyor (örneğin int x; int &&rrx = x; artık GCC'de derlenmemektedir) - 13 Temmuz 14: 14
Bir C ++ 03 referansı (şimdi C ++ 11'de lvalue referansı olarak adlandırılır) arasındaki en büyük fark, const olmak zorunda kalmadan geçici gibi bir değere bağlanabilmesidir. Dolayısıyla, bu sözdizimi artık yasaldır:
T&& r = T();
rvalue referansları öncelikle aşağıdakileri sağlar:
Anlambilimi taşı . Artık normal sabit değer referansı yerine bir değer referansı alan bir hareket yapıcı ve hareket atama operatörü tanımlanabilir. Bir taşıma, kaynağı değiştirmeden zorunlu kılmak dışında bir kopya gibi işlev görür; aslında, genellikle taşınan kaynaklara sahip olmayacak şekilde kaynağı değiştirir. Bu, özellikle standart kütüphane uygulamalarında yabancı kopyaları ortadan kaldırmak için mükemmeldir.
Örneğin, bir kopya oluşturucu şöyle görünebilir:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Bu kurucu geçici olarak geçtiyse, kopya gereksiz olacaktır çünkü geçici olanın sadece yok edileceğini biliyoruz; neden geçici olarak tahsis edilen kaynakları kullanmıyorsunuz? C ++ 03'te, geçici geçildiğimizi belirleyemediğimiz için kopyalamayı önlemenin bir yolu yoktur. C ++ 11'de, bir hareket yapıcısını aşırı yükleyebiliriz:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Buradaki büyük farka dikkat edin: hareket yapıcı aslında argümanını değiştirir. Bu, geçici olanı inşa edilen nesneye etkili bir şekilde "taşıyacak" ve böylece gereksiz kopyayı ortadan kaldıracaktır.
Taşıma yapıcısı, std::move
işlevler kullanılarak açıkça değer değer referanslarına dönüştürülen geçici dosyalar ve sabit olmayan değer referansları için kullanılır (yalnızca dönüşümü gerçekleştirir). Aşağıdaki kod her ikisi için hareket yapıcı çağırmak f1
ve f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Mükemmel yönlendirme . rvalue referansları, şablonlanmış işlevler için bağımsız değişkenleri doğru şekilde iletmemizi sağlar. Örneğin bu fabrika işlevini ele alalım:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Eğer factory<foo>(5)
çağırırsak, argüman 'yapıcısı int&
bir anahtar kelimeyi ele geçirse bile, değişmez bir 5'e bağlanmaz . Bunun yerine kullanabiliriz , ancak yapıcı argümanını const olmayan referansla alırsa ne olur ? Gerçekten jenerik fabrika işlevini hale getirmek için, üzerinde fabrika aşırı zorunda kalacak ve üzerinde . Fabrika 1 parametre türünü alırsa bu iyi olabilir, ancak her ek parametre türü 2 tarafından ayarlanan gerekli aşırı yükü çarpar. Bu çok hızlı bir şekilde sürdürülemez.foo
int
A1 const&
foo
A1&
A1 const&
rvalue referansları, standart kitaplığın std::forward
lvalue / rvalue başvurularını düzgün şekilde iletebilen bir işlev tanımlamasına izin vererek bu sorunu giderir . Nasıl std::forward
çalıştığı hakkında daha fazla bilgi için bu mükemmel yanıta bakın .
Bu, fabrika fonksiyonunu şu şekilde tanımlamamızı sağlar:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Şimdi argümanın rvalue / lvalue-ness değeri, T
yapıcısına aktarıldığında korunur . Bu, eğer fabrika bir değerle çağrılırsa, T
yapıcısına bir değerle çağrılır. Eğer fabrika bir lvalue ile çağrılırsa, T
yapıcı bir lvalue ile çağrılır. Geliştirilmiş fabrika işlevi, özel bir kural nedeniyle çalışır:
İşlev parametresi türü, şablon parametresinin T&&
bulunduğu formda T
olduğunda ve işlev bağımsız değişkeni bir tür değeri A
olduğunda, tür A&
, şablon bağımsız değişkeninin kesilmesi için kullanılır.
Böylece, biz fabrika kullanabilirsiniz gibi:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Önemli rvalue referans özellikleri :
- Aşırı yük çözünürlüğü için, değerler değerler değer referanslarına bağlanmayı ve değerler değerler değer referanslarına bağlanmayı tercih eder . Bu nedenle geçiciler neden bir kopya oluşturucu / atama işleci yerine bir taşıma yapıcısı / taşıma atama işleci çağırmayı tercih eder.
- rvalue referansları, örtük bir dönüştürme işleminin sonucu olan rvalue'lara ve geçici programlara dolaylı olarak bağlanır . yani
float f = 0f; int&& i = f;
iyi biçimlendirilmiştir, çünkü şamandıra dolaylı olarak int'e dönüştürülebilir; referans, dönüşümün sonucu olan bir geçici olacaktır.
- Adlandırılmış değer referansları değerlerdir. Adsız değer referansları değerlerdir. Bu,
std::move
çağrının neden gerekli olduğunu anlamak için önemlidir :foo&& r = foo(); foo f = std::move(r);