2016-03-10 8 views
15

std::unique_ptrを返すときは、std::moveを使用しないようにしていました。これは、RVOを禁止するためです。私はローカルstd::unique_ptrを持っているこのケースを持っていますが、戻り値のタイプはstd::shared_ptrです。 は、ここでは、コードのサンプルです:ローカルのunique_ptrをshared_ptrとして返す

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は両方のケースを受け入れますが、クランは、このエラーでgetInt1()を拒否:

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

ここでは両方のケースではcoliru上だ:GCCClang

両方コンパイラは3番目のケースを受け入れます。

どちらが間違っていますか?ありがとう。

+2

私は、あなたが必要とするものを理解したときに、より良い答えを出すことができると考えています。現時点では、これはmake_uniqueの代わりにmake_sharedを呼び出すことで簡単に解決できます。それが不可能な理由があると仮定し、その理由を理解するのに役立ちます。 – Elliott

+7

@Elliott:何が解決できるのですか?彼は何かを解決する方法を尋ねていない、彼は彼が持っているコードが標準で正しいかどうか尋ねている。 –

答えて

17

正しい答えは、あなたが話しているC++標準によって異なります。

C++ 11について言えば、clangは正しい(明示的な移動が必要です)。 C++ 14について話している場合、gccは正しい(明示的な移動は必要ありません)。

C++ 11には、N3290/[class.copy]/P32で述べている:

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, ...

これは、リターン式が関数の戻り値の型と同じ型を持っているときにのみ、暗黙的な動きを得ることを求めています。

しかし、CWG 1579がこれを変更しました。この欠陥レポートはC++ 11以降、C++ 14以降で受け入れられました。この同じ段落は今読み:

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, ...

をこの変更は、基本的には、return式の型がコンバーチブル-する関数の戻り値の型を、まだ暗黙の移動のための資格を得ることができるようになります。

これは、__cplusplusの値に基づいてコードに#if/#elseが必要であることを意味しますか?

私はそれをすることができますが、私は気にしません。私はC++ 14をターゲットにしていた場合、私はちょうどになります。コードが予期せずC++ 11コンパイラの下で実行されている場合は

return i; 

、エラーのコンパイル時に通知されます、そして、それは簡単です修正する:

return std::move(i); 

をあなただけのC++ 11をターゲットにしている場合は、moveを使用しています。

C++ 11とC++ 14(およびそれ以降)の両方をターゲットにする場合は、moveを使用してください。 moveを不必要に使用することの欠点は、RVO(戻り値の最適化)を禁止できることです。ただし、この場合、RVOは法的ではありません(return文から関数の戻り型に変換されるため)。そして、無償のmoveは何も傷つけません。

それなしで、物事はまだC++ 11でコンパイル、および動きとは反対に、高価なコピー変換を起動する場合は、C++ 14をターゲットにしても無償move方に傾く可能性がある1時間があります変換。この場合、誤ってC++ 11でコンパイルすると、静かなパフォーマンスバグが発生します。また、C++ 14でコンパイルした場合、無償のmoveには依然として有害な影響はありません。

+0

@TobySpeight:アドレッシング、ありがとう。 –

+0

C++ 11のみを使用している場合は、コードに "std :: move(...")を返すのではなく、返す前に正しい型を作成することをお勧めします。少しだけ。 – Daemin

9

std::unique_ptrは、それが右値の場合にのみstd::shared_ptrを構成するために使用することができます。 std::shared_ptrのコンストラクタ宣言を参照してください。

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

ですから、それ以外の場合は、故障した、第一ケースを機能させるためにstd::moveを使用する必要があります。

return std::move(i); 

私はgcc 4.9.3でコードをコンパイルしました。

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

こんにちは、移動が 'C++ 11'だけに必要であることを指定する答えを変更できますか? –

+0

これは関連する可能性がありますhttp://stackoverflow.com/q/18889843/985296 – stefan

関連する問題