2016-05-18 5 views
1

SFINAEを使用してメソッドのオーバーロードを行うためには、どのルールを考慮する必要があるのか​​わかりません。私は知っているより多くのルールが関与するために縫い目が存在するので、問題で何度も実行されます。ですから、何度も何度も質問するのではなく、一般的な問題を解決するために簡単に説明できる一連のルールがあることを願っています。SFINAEのオーバーロードについて考慮する必要があるルール

私のスタート地点はここにあった:私のコードを変更し、

"the "constructor cannot be overloaded" problem can be solved by adding a dummy and defaulted template parameter (like , typename Z = void) to one of the constructors"

OK: Specializing class with SFINAE if a parameter pack is needed

コード1

class AA { public: using TRAIT = int; }; 
class BB { public: using TRAIT = float; }; 

template < typename T> 
class Y 
{ 
    public: 
     template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type> 
      Y() { std::cout << "First" << std::endl; } 

     template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type> 
      Y() { std::cout << "Second" << std::endl; } 

}; 

error: 'template template Y::Y()' cannot be overloaded

この問題への私はコメントを得ました〜:

コード2

template < typename T> 
class Y 
{ 
    public: 
     template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type, typename X=int> 
      Y(X* = nullptr) { std::cout << "First" << std::endl; } 

     template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type> 
      Y() { std::cout << "Second" << std::endl; } 
}; 

OK、それがコンパイルされます。しかし、縫い目を単純化しても問題はありません。

コード3

template < typename T> 
class Y 
{ 
    public: 
     template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type> 
      Y(int* = nullptr) { std::cout << "First" << std::endl; } 

     template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type> 
      Y() { std::cout << "Second" << std::endl; } 
}; 

最後の例でも動作します! 1つのコンストラクタにデフォルトパラメータがありますが、このパラメータはテンプレート化されたものではありません。

私にとっては、ここで実行するルールを遅らせるためにどのルールが働いているのかはかなり不明です。

私の最初の誤解は、SFINAEがコンストラクタテンプレートをインスタンス化することによって行われ、1つのコンストラクタのみが利用可能になると考えたことです。これは真実ではありません!すべてのコンストラクタに設定された各パラメータは、異なる必要があります。どうして?さらに、テンプレート化されたパラメータでなければならない理由は?私の例3の縫い目は働くが、他の人は私にdummy and defaulted template parameter (like , typename Z = void)を使うよう助言を与えた。誰かが私にこのトピックに関する少しの背景情報を与えることができますか?

+0

http://stackoverflow.com/a/36500292/3953764 –

+0

常にパラメータを追加するために回避する方法は、別の署名になり

template <typename U = T, typename std::enable_if<std::is_same<int, typename U::TRAIT>::value>::type* = nullptr> Y(); template <typename U = T, typename std::enable_if<!std::is_same<int, typename U::TRAIT>::value>::type* = nullptr> Y(); 

ような何かをすることです* "私はSFINAEがコンストラクタテンプレートをインスタンス化し、コンストラクタが1つしか利用できないと考えましたが、これは真実ではありません!" *しかし、AAやBBの定義がなくてもエラーになります。したがって、この問題はテンプレートのインスタンス化には関係しません。すでに言われていることを言いましょう:単に 'void foo(bool b = false)'と 'void foo(bool b = true)'が異なるデフォルトを持っているため、定義に共存できないのと同じです。したがって、 ''と ''があります。 – HostileFork

答えて

5

関数のシグネチャが異なる必要があり、デフォルト値(正規の引数の場合はテンプレート用)はシグネチャの一部ではありません。だから、

template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type> 
Y(); 

template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type> 
Y(); 

は、それほど単純redifinition

template <typename U, typename V> 
Y(); 

template <typename U, typename V> 
Y(); 

です。

template <typename U = T, typename V = typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type, typename X=int> 
Y(X* = nullptr); 

template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type> 
Y(); 

一方

template <typename U, typename V, typename X> 
Y(X*); 

template <typename U, typename V> 
Y(); 

ので、異なるシグネチャです。

template <typename U, typename std::enable_if<std::is_same<int, typename U::TRAIT>::value>::type*> 
Y(); 

template <typename U, typename std::enable_if<!std::is_same<int, typename U::TRAIT>::value>::type*> 
Y(); 
+0

"単純に"デフォルトの初期化された型ポインタを使用する最後の例は私にとっては不思議です。私はこれが 'template 'に終わると考えました。これはどちらの定義でも同じです。 SFINAE構造の完全な表現でなぜ署名が拡大するのか、簡単な説明を追加できますか? – Klaus

+0

@Klauss 'std :: enable_if_t <..> *'が両方とも 'void * 'になっても、それは別の署名(前)です。しかし、もし両方が 'T'のために' void * 'になると、とにかくあいまいな呼び出しになります。したがって、条件は慎重に選択する必要があります(両方で同じ条件を書くには、いくつかの 'disable_if'ヘルパーを書きます)。 – Jarod42

+0

@Klaussこのようなことをはじめて見ると、static_assert(std :: is_same ::値、「テンプレートを明示的にパラメータ化できません」)を投げておくと便利だと思っていました。 SFINAEのために投げ込まれたU = Tのデフォルトを上書きするために、あなたが人々に使用させたくない呼び出しの自由度です。 *(具体的には、テンプレート化されたコンストラクタを明示的にパラメータ化することはできません(http://stackoverflow.com/a/2786963/211160))。 'Y yaa; @HostileFork:コンストラクタにテンプレートパラメータを指定できないため、コンストラクタには 'static_assert'は必要ありません。yaa.foo ();' – HostileFork

関連する問題