2013-10-09 43 views
35

Birçok durumda, bir işlevden yerel bir yeri döndürürken, RVO devreye girer. Ancak, açık bir şekilde std::move kullanılmasının RVO gerçekleşmediğinde harekete geçeceğini, ancak mümkün olduğunda RVO'nun hala uygulanacağını düşündüm. Ancak, durum böyle değil gibi görünüyor. std :: move neden RVO'yu engeller?

#include "iostream" 

class HeavyWeight 
{ 
public: 
    HeavyWeight() 
    { 
     std::cout << "ctor" << std::endl; 
    } 

    HeavyWeight(const HeavyWeight& other) 
    { 
     std::cout << "copy" << std::endl; 
    } 

    HeavyWeight(HeavyWeight&& other) 
    { 
     std::cout << "move" << std::endl; 
    } 
}; 

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return heavy; 
} 

int main() 
{ 
    auto heavy = MakeHeavy(); 
    return 0; 
} 

VC++ 11 ve GCC 4.71, hatalarını bulması ve salma ( -O2) yapılandırma ile bu kodu test edilmiştir. Kopya cetesi hiç çağrılmadı. Hareket ctor, hata ayıklama yapılandırmasında yalnızca VC++ 11 ile çağrılır. Aslında, özellikle bu derleyicilerle her şey iyi görünüyor, fakat bilgime göre, RVO isteğe bağlıdır.

Ancak, açıkça move kullanıyorsanız:

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return std::move(heavy); 
} 

hareket ctor daima

denir. Yani bunu "güvenli" yapmaya çalışmak onu daha da kötüleştiriyor.

Sorularım şunlardır:
- Neden std::move RVO'yu engelliyor?
- Ne zaman "en iyisini umuyorum" ve RVO'ya güvenmek daha iyidir ve ne zaman std::move'u kullanmalıyım? Ya da başka bir deyişle, derleyici optimizasyonunun işini yapmasına nasıl izin verebilirim ve hala RVO uygulanmazsa harekete geçmeyi nasıl sağlayabilirim?

+4

İnsanlar neden bu günlerde “en iyisi için umut” den bahsediyor? C++ 11 desteğini kullanan ancak RVO'yu düzgün bir şekilde kullanamayan ne tür bir derleyici kullanıyorlar? –

+1

Kopyalama seçimi (RVO'nun arkasındaki mekanizma) yalnızca belirli koşullar altında izin verilir. Std :: move kelimesinin yazılması, bu koşulların yerine getirilmesini engeller. –

+2

@KerrekSB Ve std :: move tarafından engellenen bu koşullar ...? – cdoubleplusgood

cevap

25

olgu Standardı (sürümü N3690) bölümünde 12.8 §31 bulunur:

belirli kriterler karşılandığında

, bir uygulama kopyalama/taşıma atlamak için izin verilir Kopyalama/taşıma işlemi için seçilen kurucu ve/veya nesnenin yıkıcısı yan etkilere sahip olsa bile, bir sınıf nesnesinin oluşturulması. Bu gibi durumlarda, uygulama, ihmal edilen kopyalama/taşıma işleminin kaynağını ve hedefini, aynı nesneye atıfta bulunmak için iki farklı yol olarak ele alır ve bu nesnenin tahribatı, iki nesnenin ne zaman olacağı, daha sonraki zamanlarda gerçekleşir. optimizasyon olmadan yok edildi. kopya elision denilen kopyalama/taşıma işlemlerinin Bu elision, (birden çok kopya ortadan kaldırmak için birleştirilebilir) aşağıdaki durumlarda izin verilir: Bir sınıf dönüş türüne sahip bir işlevde bir return açıklamada

  • , ifadesi, işlev döndürme türüyle aynı cv-niteliksiz tipte bir uçucu olmayan otomatik nesnenin (bir işlev veya yakalama yan tümcesi parametresi dışında) adıysa, otomatik nesneyi yapılandırarak kopyalama/taşıma işlemi ihmal edilebilir Fonksiyonu olmayan bir geçici sınıf nesnesi olduğunda
  • [...]
  • Bir referansa bağlı (12.2), aynı cv-niteliksiz tipte bir sınıf nesnesine kopyalanacak/taşınacak, kopyalama/taşıma işlemi, geçici nesneyi doğrudan ihmal edilen kopya/hareketin hedefine dönüştürerek ihmal edilebilir
  • [...]

(Ben dışarı bıraktı iki durum atma ve Optimizasyon açısından daha az öneme istisna nesneleri yakalamak durumunda bakınız.)

Dolayısıyla bir dönüş ifadesi kopya elision sadece oluşabilir, ifade, yerel değişkeninin adıysa.std::move(var) yazıyorsanız, artık bir değişkenin adı değil. Bu nedenle, derleyici, standarda uygun olması halinde, hareketi elemek mümkün değildir.

Stephan T. Lavavej bu konuda Going Native 2013 numaralı telefondan bahsetti ve durumunuzu tam olarak açıkladı ve neden std::move() dan kaçınmak için. 38:04 dakikasında izlemeye başla. Temel olarak, dönüş türünün bir yerel değişkenini döndürürken, bu genellikle bir değer olarak kabul edilir, dolayısıyla varsayılan olarak harekete geçirme sağlanır.

+0

Bu, ümit ettiğim cevaptı. Durumdan memnun değilim ama daha iyi anlıyorum. – cdoubleplusgood

+5

Muhtemelen düzeltmeliyiz ki 'dönüş std :: move' seçilebilir. C++ 'ya bir fonksiyonun referans dönüş değerinin, fonksiyonun belirli bir referans giriş değeriyle aynı olduğunu garanti edersek, birkaç ilginç sonuç ortaya çıkabilir. (Önemsiz ifadelerden elemen, geçici giriş argümanının ömür boyu süre uzatmadan bir fonksiyona dönüşü, iki için: ikincisi IMHO daha önemlidir). – Yakk

+0

Evet, bunu yapamamanın mantıklı olduğunu anlamıyorum. Derleyici yazarlara daha fazla zaman ayırmak daha mı zor? – Adrian

13

Derleyici optimizasyonunun çalışmasına nasıl izin verebilirim ve hala RVO uygulanmadığında harekete geçmeye devam edebilirim? Bunun gibi

: bir hamle içine dönüşünü Dönüşüm

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return heavy; 
} 

zorunludur. kopyalama ve taşıma elision izin

+0

Bu yüzden, bir yerel iadenin en kötü durumda bir hamle olması garanti edilir ve RVO en iyi durumda mı geçerlidir? – cdoubleplusgood

+2

@cdoubleplusgood: Evet. – Xeo

+0

Sanırım, 'get std :: move';) – goji

İlgili konular