2016-12-07 9 views
2

私は、テンプレートパラメータを特定のパラメータで構築できるかどうかによって、異なる専門化を持つサンプルクラスを作成しようとしています。私のサンプルでは、​​単純なintと。私は試しました:SFINAEコンストラクタパラメータ

template<class A_t> 
struct creator 
{ 
    A_t* ptr = nullptr; 
}; 

template<class A_t> 
struct creator<decltype(A_t(int()))> 
{ 
    A_t* ptr = new A_t(5); 
}; 

struct A 
{ 
    int i; 

    A(int i) : i(i) {} 
}; 

int main() { 
    std::cout << creator<A>().ptr << std::endl; 

    return 0; 
} 

私が意図したのは、自動的に構築されたオブジェクトのメモリアドレスを出力することです。しかし、それは0をプリントしている。だから、それは非特殊なテンプレートを取っている。

A_tは、特に、A_tが明示的に指定されているため、その構文で推論できます。また、decltype(A_t(int())(たとえばなくA_t&&A_t型を持つ、単純なテスト:

std::cout << std::is_same<decltype(A(int()), A>::value << std::endl; 

プリント1.

しかし、その実装が動作する2つのクラスで

#include <iostream> 

template<class A_t, class = A_t> 
struct creator 
{ 
    A_t* ptr = nullptr; 
}; 

template<class A_t> 
struct creator<A_t, decltype(A_t(int()))> 
{ 
    A_t* ptr = new A_t(5); 
}; 

struct A 
{ 
    int i; 

    A(int i) : i(i) {} 
}; 

int main() { 
    std::cout << creator<A>().ptr << std::endl; 

    return 0; 
} 

Coliru試験1つはパラメータとしてintを受け取り、もう1つは受け入れません。

なぜ最初の方法が機能しないのですか?

+1

"A_tはその構文で推論可能です" - [明らかにclangはあなたと意見が違う](http://coliru.stacked-crooked.com/a/ef8e660b39987927)。私は理由について考える必要があるだろう(会議では注意を払うことになっている= P)。 – WhozCraig

答えて

1

あなたの質問の背後にある思考の私の最高の光沢がある:特定のインスタンス化タイプU =>A_tについては

、変換 U(int)が定義されていない場合、その後置換がU(int) =>A_t(int) コンテキストdecltype(A_t(int()))に失敗しますそして特殊化:

が削除され、基本テンプレートのインスタンス化だけが残されます。しかし、 変換U(int)が定義されていれば、置換は成功するでしょう。

はその後、理由:

std::is_same<U,decltype(U(int()))>::value == true 

両方の候補者:

// Tweedledum, with of U => A_t 
struct creator<U> 
{ 
    U* ptr = nullptr; 
}; 

と:

// Tweedledee, with U => decltype(A_t(int())) 
struct creator<U> 
{ 
    U* ptr = new U(5); 
}; 

は、実行中です。 =>decltype(A_t(int()))Uを満たすため

そして、最も専門候補が選択され、より正確 裸U => A_tよりUを拘束Tweedledee、 をされます。

この推論は、それがTeedledeedecltype(A_t(int())) = UそのときU =>A_tのために推論であることに暗黙的に依存しています。 U(int) = A_t(int)が単独のテンプレート引数decltype(A_t(int()))であることが分かります。

gccがどのようにしてTweedledumを選ぶことができると思いますか?

@WhozCraigが指摘したように、打ち鳴らすは++明示あなたのdeducibilityのpremissを拒否: -

$ clang++-3.8 -Wall -Wextra -pedantic -std=c++14 main.cpp 
main.cpp:10:8: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used 
struct creator<decltype(A_t(int()))> 
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
main.cpp:9:16: note: non-deducible template parameter 'A_t' 
template<class A_t> 
      ^

So does MSVC++

そしてそれは、gccが あなたSFINAEのことばづかいの不運なフレージングで、ここでの診断経過に誘導されたことが判明し、decltype(A_t(int()))

あなたはdecltype(A_t{int()})でそれを置き換える場合は、GCCもgets with the program

$ g++-6 -Wall -Wextra -pedantic -std=c++14 main.cpp 
main.cpp:10:8: error: template parameters not deducible in partial specialization: 
struct creator<decltype(A_t{int()})> 
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
main.cpp:10:8: note:   ‘A_t’ 

その後、clang ++は3つのコンパイラのうち、 をコンパイルする唯一のコンパイラであり、ベーステンプレートからcreator<A>をインスタンス化します。

decltype(A_t{int()})A_tある推論ないがC++ 14標準によって承認されていることをこの最終的コンセンサス:[temp.deduct.type]

タイプからテンプレート引数を推定

1テンプレート引数はいくつかの異なるコンテキストで導き出すことができますが、いずれの場合も テンプレートパラメータ(Pと呼ぶ)で指定された型は と実際の型(Aと呼ぶ)と比較され、試行されます の値を代入した後に、テンプレート引数 の値(タイプパラメータのタイプ、非タイプのパラメータの値、またはテンプレートパラメータの テンプレート)を検索します(これを演繹A )、多くの場合 ... A.

との互換性は、種類、テンプレート、および非タイプの値は Pはテンプレート引数控除に参加...特定のコンテキストで構成するのに使用されますしかし、 この値は型控除には関与しませんが、代わりに他の場所で明示的に指定されたテンプレート引数の値 を使用しますd。 テンプレートパラメータが推定されていないコンテキストでのみ使用され、明示的に が指定されていない場合、テンプレート引数の減算は失敗します。

5非推測コンテキストは、次のとおりです。

...

(5.2) - decltype指定子の発現。

...

パラ2では、2番目のアプローチが成功する理由も説明しています。