7

最近、完全な転送コンストラクターを使用してクラス階層を実装しようとしたときに問題が発生しました。次の例を考えてみましょう。

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&&
4

3 に答える 3

2

に変更Test(const Test& other) : TestBase(other) {}してみてくださいTest(const Test& other) : TestBase(static_cast<TestBase const&>(other)) {}

2番目のTestコンストラクターはTestBaseを呼び出しており、2つの可能性があります。それらの1つは何でも取り、もう1つはTestBaseを取ります。しかし、あなたはそれにテストに合格しています-「何でも」がよりよく一致します。TestBase const&に明示的にキャストすることで、一致する適切なものを取得できるはずです。

別の可能性としては、Testの構築方法が関係している可能性があります。渡したものが、代わりにテンプレートコンストラクターとTestに一致している可能性があります。Testからテンプレートコンストラクターを削除し、エラーがなくなるかどうかを確認することで、この他の可能性をテストできます。

その場合、リンクした手法(推定された型がTestと一致するときにTestテンプレートコンストラクターを無効にする)が機能しないのはなぜですか?

于 2012-10-31T15:18:49.430 に答える
1

以下は機能するはずであり、明示的なキャストを使用しません。

struct Test : public TestBase {
  private: 
  static TestBase const& toBase(const Test& o) { return o; }

  public:
  template <typename T>
  explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}

  Test(const Test& other) : TestBase(toBase(other)) {} 
};
于 2012-10-31T15:47:53.260 に答える
1

エラーメッセージを詳しく見てみましょう。

std::basic_string<...>::basic_string(const std::basic_string<...> &) :

つまり、のコピーコンストラクタに適用されますstd::string

cannot convert parameter 1 from 'const Test' to 'const std::basic_string<..> &

確かに、からに変換する方法はありませTeststd::string。ただし、Test文字列メンバー、つまり。がありstd::string s;ます。

.s結論:その場所に追加するのを忘れたようです。おそらく、にありs(std::forward<T>(t))ます。

もう1つの考えられる理由は、のインスタンスをコピー構築するために、コンストラクターの2番目のオーバーロードではなく1番目のオーバーロードが選択されたことですTest

于 2012-10-31T15:12:44.327 に答える