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 Tyine 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ıç tdeğ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(), tdirekt 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 FactoryFunctiongetiri Tdeğ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, tyine 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.