Kopya seçiminin bazı durumlarda yapılmasına izin verildi. Bununla birlikte, izin verilmiş olsa bile, kodun yine de kopyası çıkarılmamış gibi çalışabilmesi gerekiyordu. Yani, erişilebilir bir kopya ve / veya taşıma oluşturucu olması gerekiyordu.
Garantili kopya seçimi, bir dizi C ++ kavramını yeniden tanımlar, öyle ki kopyaların / hareketlerin elenebileceği belirli koşullar aslında bir kopyayı / taşımayı hiç teşvik etmez . Derleyici bir kopyayı elden çıkarmıyor; standart, böyle bir kopyalamanın asla olamayacağını söylüyor.
Bu işlevi düşünün:
T Func() {return T();}
Garanti edilmeyen kopya seçim kurallarına göre, bu bir geçici oluşturacak ve sonra bu geçiciden işlevin dönüş değerine geçecektir. O hareket operasyonu olabilir elided edilmesi, ancak T
yine de hiç kullanılmamış olsa bile erişilebilir bir hamle yapıcı olması gerekir.
Benzer şekilde:
T t = Func();
Bu, kopyasının başlatılmasıdır t
. Bu, başlangıç t
değerinin dönüş değeri ile kopyalayacaktırFunc
. Ancak, T
çağrılmasa bile yine de bir hareket oluşturucuya sahip olmak gerekir.
Garantili kopya seçimi , bir prvalue ifadesinin anlamını yeniden tanımlar . Pre-C ++ 17, prvalues geçici nesnelerdir. C ++ 17'de, bir prvalue ifadesi yalnızca geçici gerçekleştirebilen bir , ancak henüz geçici değildir.
Prvalue türünden bir nesneyi başlatmak için bir prvalue kullanırsanız, o zaman hiçbir geçici gerçekleşmez. Ne zaman yaparsanreturn T();
, bu, işlevin dönüş değerini bir prvalue aracılığıyla başlatır. Bu işlev döndüğünden T
, geçici oluşturulmaz; prvalue'nun ilklendirilmesi, dönüş değerini doğrudan doğrudan başlatır.
Anlaşılması gereken şey, dönüş değeri bir prdeğer olduğu için henüz bir nesne olmadığıdır . Sadece bir nesne için bir başlatıcıdır, tıpkıT()
.
Bunu yaptığınızda T t = Func();
, dönüş değerinin pr değeri doğrudan nesneyi başlatır t
; "geçici oluştur ve kopyala / taşı" aşaması yoktur. Yana Func()
bireyin dönüş değeri ile bir prvalue eşdeğerdir T()
, t
direkt olarak başlatılırT()
size yapmış aynen sankiT t = T()
.
Bir prvalue başka bir şekilde kullanılırsa, prvalue geçici bir nesneyi somutlaştırır ve bu o ifadede kullanılır (veya ifade yoksa atılır). Öyleyse, eğer yaptıysanız const T &rt = Func();
, prvalue T()
, referansı içinde saklanacak olan geçici ( başlatıcı olarak kullanarak ) gerçekleştirirrt
olağan geçici ömür boyu uzatma öğeleri ile birlikte .
Kesin seçimin yapmanıza izin verdiği bir şey, hareketsiz nesneleri iade etmektir. Örneğin,lock_guard
kopyalanamaz veya taşınamaz, bu nedenle onu değere göre döndüren bir işleve sahip olamazsınız. Ancak garantili kopya seçimi ile bunu yapabilirsiniz.
Garantili seçim, doğrudan başlatma ile de çalışır:
new T(FactoryFunction());
Eğer FactoryFunction
getiri T
değeriyle, bu ifadenin ayrılan belleğe dönüş değeri kopyalamak olmaz. Bunun yerine bellek ayıracak ve ayrılmış belleği doğrudan işlev çağrısı için geri dönüş değeri belleği olarak kullanacaktır .
Dolayısıyla, değere göre dönen fabrika işlevleri, yığın tahsisli belleği, farkında bile olmadan doğrudan başlatabilir. Tabii ki, bu işlevler dahili olarak garantili kopya seçimi kurallarını takip ettikleri sürece . Bir tür prvalue döndürmeleri gerekir T
.
Elbette bu da işe yarar:
new auto(FactoryFunction());
Daktilo adlarını yazmaktan hoşlanmıyorsanız.
Yukarıdaki garantilerin yalnızca pr değerler için çalıştığını kabul etmek önemlidir. Yani, adlandırılmış bir değişkeni döndürürken hiçbir garanti alamazsınız :
T Func()
{
T t = ...;
...
return t;
}
Bu durumda, t
yine de erişilebilir bir kopyalama / taşıma yapıcısına sahip olmalıdır. Evet, derleyici kopyalama / taşımayı optimize etmeyi seçebilir. Ancak derleyicinin yine de erişilebilir bir kopyalama / taşıma yapıcısının varlığını doğrulaması gerekir.
Dolayısıyla adlandırılmış dönüş değeri optimizasyonu (NRVO) için hiçbir şey değişmez.