2016-03-10 21 views
15

std::unique_ptr'u döndürürken, std::move kullanmıyorum, çünkü bunu yapmak RVO'yu engeller. Yerel bir std::unique_ptr sahip olduğum bu durumda sahibim, ancak dönüş türü std::shared_ptr'dur. İşte kod örneği verilmiştir:Yerel unique_ptr'yi bir shared_ptr olarak döndürme

shared_ptr<int> getInt1() { 
    auto i = make_unique<int>(); 

    *i = 1; 

    return i; 
} 

shared_ptr<int> getInt2() { 
    return make_unique<int>(2); 
} 

unique_ptr<int> getInt3() { 
    auto ptr = make_unique<int>(2); 

    return ptr; 
} 

int main() { 
    cout << *getInt1() << endl << *getInt2() << *getInt3() << endl; 
    return 0; 
} 

GCC hem davaları kabul eder, ama Clang bu hata ile getInt1() reddediyor: GCCClang

ikisi:

İşte
main.cpp:10:13: error: no viable conversion from 'std::unique_ptr<int, std::default_delete<int> >' to 'shared_ptr<int>' 
    return i; 
     ^

coliru hem vakalar yüzünden derleyici üçüncü davayı kabul eder.

Hangisi yanlış? Teşekkürler. Yalnızca bir rengin olduğunda std::shared_ptr oluşturmak için

+2

Bunun için neye ihtiyacınız olduğunu anladığımızda daha iyi cevaplar verebileceğimizden şüpheleniyorum. Şu anda, bu make_unique yerine make_shared çağrılarak önemsiz bir şekilde çözülebilir. Bunun mümkün olmadığının bir sebebi var ve bu sebebi anlamaya yardımcı olur. – Elliott

+7

@Elliott: Ne çözülebilir? Bir şeyi nasıl çözeceğini sormuyor, sahip olduğu kodun standartlara uygun olup olmadığını soruyor. –

cevap

17

Doğru yanıt, söz konusu C++ standardına bağlıdır.

C++ 11 hakkında konuşuyorsak, clang doğrudur (açık bir harekete ihtiyaç vardır). C++ 14 hakkında konuşuyorsak, gcc doğrudur (açık bir harekete gerek yoktur).

C++ 11 N3290/[class.copy]/P32 diyor:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, ...

Bu dönüş ifadesi işlev dönüş türü ile aynı türe sahip olduğunda sadece örtülü hareketlen ister.

Ancak, CWG 157910 bunu değiştirdi ve bu hata raporu C++ 11'den sonra ve C++ 14 için de kabul edildi. Bu, aynı paragraf şimdi okur:

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, ...

Bu modifikasyon temelde dönüş ifadesi tipi olmasını sağlar konvertibl-işlev dönüş türü ve hala örtülü hareket için uygun olması.

Bu, kodun __cplusplus değerine bağlı olarak #if/#else gereksinimine sahip olduğu anlamına mı geliyor?

Bunu yapabilir, ama rahatsız etmem.Ben C++ 14 hedef olsaydı, ben sadece olurdu: Kod beklenmedik bir C++ 11 derleyici altında çalıştırılır

return i; 

varsa, hata derleme sırasında haberdar ve bu önemsiz olacak düzeltmek için:

return std::move(i); 

sadece C++ 11 hedefliyorsanız, move kullanın.

Hem C++ 11 hem de C++ 14 (ve ötesini) hedeflemek isterseniz, move'u kullanın. move'u kullanmanın olumsuz tarafı, RVO'yu (Dönüş Değeri Optimizasyonu) engelleyebileceğinizdir. Ancak, bu durumda, RVO bile yasal değildir (return ifadesinden işlevin dönüş türüne dönüşümü nedeniyle). Ve bu nedenle, bedava move hiçbir şeye zarar vermez. o olmadan şeyler hala C++ 11 yılında derlemek ve pahalı bir kopya dönüşüm çağırmak durumunda hareket aksine

C++ 14 hedefleme bile bir karşılıksız move meyledeceğini olabilecek bir zamandır, dönüşümü. Bu durumda, yanlışlıkla C++ 11 altında derleme sessiz bir performans hatası oluşturur. Ve C++ 14 altında derlendiğinde, karşılıksız move'un hala zararlı etkileri yoktur.

+0

@TobySpeight adresinde bulunabilir: Adreslenmiş, teşekkürler. –

+0

Sadece C++ 11 kullanıyorsanız, kodunuzda "std :: move (..." ifadesini döndürmek yerine, döndürmeden önce doğru türü oluşturmaya teşvik edeceğim. Çünkü derleyici yalnızca bir hareket etmek yerine NRVO yapabilir Birkaç kez. – Daemin

9

kullanılabilir. std::shared_ptr yapıcısı beyanı Bkz:

template< class Y, class Deleter > 
shared_ptr(std::unique_ptr<Y,Deleter>&& r); 

Yani aksi takdirde başarısız olması, 1 vaka çalışması için std::move kullanmak gerekir.

return std::move(i); 

BTW: o da başarısız gcc 4.9.3 ile kod derlenmiş.

source_file.cpp:14:12: error: cannot bind ‘std::unique_ptr<int, std::default_delete<int> >’ 
lvalue to ‘std::unique_ptr<int, std::default_delete<int> >&&’ 
    return i; 
      ^
+0

Merhaba, hareketin yalnızca "C++ 11" için gerekli olduğunu belirtmek için cevabınızı değiştirebilir misiniz? –

+0

Bu, ilgili http://stackoverflow.com/q/18889843/985296 – stefan