次のクラスの階層があるとしましょう。
struct Base
{
};
struct Derived : public Base
{
void DoStuffSpecificToDerivedClass()
{
}
};
そして、次のファクトリメソッド:
std::unique_ptr<Base> factoryMethod()
{
auto derived = std::make_unique<Derived>();
derived->DoStuffSpecificToDerivedClass();
return derived; // does not compile
}
問題は、return
ステートメントがコンパイルされないことです。std::unique_ptr
には共分散をサポートするコピーコンストラクターがないため(コピーコンストラクターがないため意味があります)、移動するだけです。共分散をサポートするコンストラクター。
この問題を解決するための最良の方法は何ですか?私は2つの方法を考えることができます:
return std::move(derived); // this compiles
return std::unique_ptr<Base>(derived.release()); // and this compiles too
編集1:コンパイラとしてVisual C++ 2013を使用しています。 return derived
の元のエラーメッセージは次のようになります。
Error 1 error C2664: 'std::unique_ptr<Base,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : cannot convert argument 1 from 'std::unique_ptr<Derived,std::default_delete<Derived>>' to 'std::unique_ptr<Derived,std::default_delete<Derived>> &&'
編集2:これは、標準のVS2013テンプレートから新しく作成されたコンソールアプリです。コンパイラの設定は調整していません。コンパイラのコマンドラインは次のようになります。
デバッグ:
/Yu"stdafx.h" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm /Od /sdl /Fd"Debug\vc120.pdb" /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:Prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\CppApplication1.pch"
リリース:
/Yu"stdafx.h" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"Release\vc120.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:Prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\CppApplication1.pch"
あなたはこれを行うことができます:
return std::move(derived);
このようにして、unique_ptr
の要件を満たすコピーは不要であることをコンパイラーに通知します。タイプが完全に一致する場合は、move
を明示的に指定する必要はありませんが、この場合は指定します。
質問で述べたように、問題は、returnステートメントがコンパイルされない、std::unique_ptr
に共分散をサポートするコピーコンストラクターがない、共分散をサポートする移動コンストラクターがあるだけですが、コンパイラーはまだ移動しないということですstd::unique_ptr<Derived>
から。
これは、関数から返されたオブジェクトから移動するための条件がコピーの省略の基準と密接に関連しているためです。このため、返されるオブジェクトの型は関数の戻り値の型と同じである必要があります。
[class.copy]/32:
ソースオブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値で指定されていることを除いて、コピー操作の省略基準が満たされているか、満たされる場合、コピーのコンストラクターを選択するためのオーバーロード解決は次のようになります。最初は、オブジェクトが右辺値で指定されているかのように実行されました。
したがって、私は好みます、
return std::move(derived);
ただし、 DR-9R5 にルールの変更があり、タイプが同じでない場合でも戻り値が右辺値として扱われるようになっています。gcc-5がルールを実装しているため、ルールを実装する必要はありません。図のようにgcc-5のコードを変更します ここ 。