6

次のプログラムの出力...

#include <iostream>

using namespace std;

struct X
{
    X(const X&)              { cout << "copy" << endl; }
    X(X&&)                   { cout << "move" << endl; }

    template<class T> X(T&&) { cout << "tmpl" << endl; }
};

int main()
{
    X x1 = 42;
    X x2(x1);
}

tmpl
tmpl

望ましい出力は次のとおりです。

tmpl
copy

具体的なコピー コンストラクターがテンプレート コンストラクターよりも優先されないのはなぜですか?

コピー コンストラクターとムーブ コンストラクターのオーバーロードがテンプレート コンストラクターよりも優先されるように修正する方法はありますか?

4

3 に答える 3

5

まあ、それは参照の崩壊のためです。

過負荷解決段階では、関数テンプレートがインスタンス化Tされると、と見なされますX&。したがって、T&&(はX& &&)はX&参照の折りたたみによるものになり、関数テンプレートからインスタンス化された関数は完全に一致し、コピーコンストラクターはからの変換を必要X&とします。 to const X&(これが劣った一致として選択されない理由です)。

ただし、コピーコンストラクターからを削除するconstと、コピーコンストラクターが優先されます。これを試して:

X(/*const*/ X&) { cout << "copy" << endl; }

期待どおりに出力します。

または、関数テンプレートでパラメーターを次のように作成const T&すると、(同じままであっても)copy-constructorが呼び出されます。これは、参照の折りたたみが表示されないためです。

template<class T> X(const T &) { cout << "tmpl" << endl; }

再び出力が期待されます。

于 2012-12-31T10:13:06.267 に答える
3

(他の回答が示唆しているように)別のコンストラクターを追加したくない場合は、SFINAEを使用して、テンプレートコンストラクターを次のように置き換えることにより、呼び出しを制限できます。

template<class T
    , typename std::enable_if<not std::is_same<X, typename std::decay<T>::type>::value, int>::type = 0 
> X(T&&) { cout << "tmpl " << endl; }

dummyこれには、デフォルトのテンプレート引数を追加するだけです(既知の手法: http ://www.boost.org/doc/libs/1_52_0/libs/utility/enable_if.html )。追加のヘッダーは必要ありません。

目的の出力が得られます。

私は関連する問題からこの答えを得ました:http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html。これはすべてかなりエレガントに見えませんが、今のところ唯一の方法のようです。私はまだもっとエレガントな解決策を見たいと思っています。

于 2012-12-31T10:50:32.873 に答える
2

コンストラクターを選択する場合でも、通常の過負荷解決ルールが適用されます。非const左辺値参照を取得するコンストラクター(引数控除後のテンプレートコンストラクターの場合)は、const左辺値参照を取得するコンストラクターよりも適切に一致します。

もちろん、非定数の左辺値参照を使用して別のオーバーロードを追加することもできます。

X(X&)              { cout << "copy" << endl; }

更新:テンプレートコンストラクターがより適切に一致するその他のケース:

const X f()
{ return X(); }

struct Y : X
{ Y() { } };

int main()
{
  X x3(f()); // const-qualified rvalue
  Y y;
  X x4(y); // derived class
}
于 2012-12-31T10:14:30.327 に答える