最近、完全な転送コンストラクターを使用してクラス階層を実装しようとしたときに問題が発生しました。次の例を考えてみましょう。
struct TestBase {
template<typename T>
explicit TestBase(T&& t) : s(std::forward<T>(t)) {} // Compiler refers to this line in the error message
TestBase(const TestBase& other) : s(other.s) {}
std::string s;
};
struct Test : public TestBase {
template<typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(other) {}
};
コードをコンパイルしようとすると、次のエラーが発生します。
エラー3エラーC2664:'std :: basic_string <_Elem、_Traits、_Alloc> :: basic_string(const std :: basic_string <_Elem、_Traits、_Alloc>&)':パラメータ1を'constTest'から'conststdに変換できません:: basic_string <_Elem、_Traits、_Alloc>& '
私の理解では、コンパイラーは完全な転送コンストラクターをコピーコンストラクターよりも優れた数学として扱います。たとえば、Scott Meyers:Copying Constructors in C++11を参照してください。クラス階層のない他の実装では、完全な転送コンストラクターがSFINAEを介したコピーコンストラクターにならないようにすることができます。たとえば、Martinho Fernandes:転送コンストラクターに関するいくつかの落とし穴を参照してください。上記の解決策をこの例に適用しようとすると、同じエラーメッセージでコンパイルできません。
考えられる解決策の1つは、完全な転送を回避し、コンストラクターでパラメーターを値で取得してから、それらからクラス変数に移動することだと思います。
だから私の質問は、この問題に対する他の解決策があるかどうか、またはそのような場合に完全な転送が不可能かどうかです。
更新: 私の質問は誤解されやすいことがわかりました。それで、私は私の意図と文脈を少し明確にしようとします。
- コードは質問に投稿されたように完全です。作成された他のオブジェクトや呼び出された関数はありません。投稿された例をコンパイルしようとしたときにエラーが発生しました。
- 完全な転送コンストラクターを持つことの目的は、メンバーの初期化であり、ある種の余分なコピーコンストラクターを持つことではありません。ここでの理由は、一時的なオブジェクトでメンバーを初期化するときにいくつかのオブジェクトのコピーを保存するためです(Scott Meyersによる講演で提案されたように)
- 残念ながら、完全な転送コンストラクターは、他のオーバーロードされたコンストラクター(この例ではコピーコンストラクター)と競合する可能性があります。
- 提案されたこの質問への回答とコメントのように:ここで考えられる解決策は、明示的なキャストを導入するか、テンプレート化されていないコンストラクターを個別に持つことです(つまり、それぞれパラメーターを持つ2つのコンストラクターを持つ例に関して
const string&
)string&&
。