2017-09-20 10 views
1

テンプレート化された関数を作成しようとしていますが、コンパイル時に特殊化のみを使用するように強制されています。私は、std::false_typeから継承したものにstatic_assertを使用することを示唆するForce a compile time error in a template specializationを参照しました。デフォルトテンプレートはstatic_assertにもかかわらず一致します

#include <iostream> 
using namespace std; 

template<typename T> 
struct always_false : std::false_type {}; 

//Case: Default 
template<typename T> 
void foo(T val) { 
    static_assert(always_false<T>::value, "");  
} 

//Case: bool 
template<> 
void foo<bool>(bool val) { 
    cout << "Is explicitly a bool! " << val << endl; 
} 

//Case: int 
template<typename T, typename std::enable_if<!std::is_same<T,bool>::value && std::is_convertible<T,int>::value,int>::type=0> 
void foo(T val) { 
    cout << "Can be implicitly converted to int! " << (int)val << endl; 
} 

int main() { 
    foo(true); //(Good) Works correctly 
    foo((int)5); //(Bad) Error: call of overload foo(int) is ambiguous 
    foo((unsigned int)10); //(Bad) Error: call of overload foo(unsigned int) is ambiguous 
    foo((void*)nullptr); //(Good) Error: static assertion failed 
    return 0; 
} 

私はintまたはunsigned intに渡すと、コンパイラは呼び出しが、それはCase: DefaultまたはCase: intのいずれかを使用することができることを曖昧示唆されていることを訴えます。

Case: Defaultにはalways_falsestatic_assert()が含まれているので、これは混乱しています。コンパイラはこれを許可しません。

私の最後の例では、void*を渡すと正常にstatic_assert()がトリガされ、コンパイル時にエラーが発生します。

私はSFINAEテンプレートメタプログラミングを使用したプログラミングに新しいですので、私は、私はCase: int専門で何か間違っ

二つの質問やっている疑いがある:

  • をなぜ、このコードでfoo(int)曖昧ですか?
  • テンプレートを使用して、この望ましい動作(明示的bool特殊化+暗黙の整数特殊化)を取得するより良い方法はありますか?
+0

あなたはテンプレートの特殊化、それがでインスタンス化された任意の型に対して有効でなくても、プライマリ1を宣言することはできません。診断は必要ありません。 – StoryTeller

答えて

1

なぜこのコードのfoo(int)はあいまいですか?

static_assert()のバージョンは選択されてもエラーが発生するため、まだ存在します。コンパイラーは汎用バージョンまたは整数対応バージョンを選択するかどうかを知りません。

この望ましい動作(明示的なブール特殊化+暗黙のint特殊化)を得るために、テンプレートを使用するより良い方法はありますか? EDIT - -

可能な方法は、一般的なバージョンを避け、SFINAE次は

#include <iostream> 
#include <type_traits> 

template <typename T> 
typename std::enable_if<std::is_same<T, bool>::value>::type foo(T val) 
{ std::cout << "bool case " << val << std::endl; } 

template <typename T> 
typename std::enable_if< ! std::is_same<T, bool>::value 
    && std::is_convertible<T, int>::value>::type foo(T val) 
{ std::cout << "integer case " << (int)val << std::endl; } 

int main() 
{ 
    foo(true); // bool case 
    foo(1);  // integer case 
    foo(2U); // integer case 
    foo(3L); // integer case 
    foo(4UL); // integer case 
    foo(5LL); // integer case 
    foo(6ULL); // integer case 

    // foo((void*)nullptr); // compilation error 
} 

フル実施例である

を必要とするバージョンを有効にすることです

OP

申し訳ありませんが、私はまだ混乱しています。あなたは詳しく説明できますか?私は、SFINAEのために、代替でエラーが発生した場合、他のテンプレートを使用すると考えました。

正確に。 でない場合、コンパイラは同じテンプレートの2つの異なるバージョンの中から選択する必要があります。

私は意味:あなたはfoo(5)を呼び出したときに、あなたの例では、エラーが

typename std::enable_if<!std::is_same<T,bool>::value 
    && std::is_convertible<T,int>::value,int>::type=0> 

の置換ではありませんので、コンパイラはその2つのテンプレート関数の間

template<typename T> 
void foo(T val) { 
    static_assert(always_false<T>::value, "");  
} 

//Case: int 
template<typename T, int = 0> 
void foo(T val) { 
    cout << "Can be implicitly converted to int! " << (int)val << endl; 
} 

を選択する必要がありますデフォルト値のテンプレート値のみが異なるため、(コンパイラの観点からは)区別できません。

そして

template<> 
void foo<bool>(bool val) { 
    cout << "Is explicitly a bool! " << val << endl; 
} 

は(フル)テンプレート特殊ですが、

//Case: int 
template<typename T, int = 0> 
void foo(T val) { 
    cout << "Can be implicitly converted to int! " << (int)val << endl; 
} 

は、テンプレートの特殊化(機能のないテンプレートの部分特殊化は、C++ 11で認められないではないことを確認/ 14/17;構造体/クラスのみを部分的に限定することができます)。一般的なテンプレートです。

+0

"static_assert()を持つバージョンは選択されてもエラーが発生するため、コンパイラは汎用バージョンまたは整数対応バージョンを選択するかどうかを認識しません。 申し訳ありませんが、私はまだ混乱しています。あなたは詳しく説明できますか?私は、SFINAEのために、代替でエラーが発生した場合、他のテンプレートを使用すると考えました。 – Ross

+0

ソリューションのおかげで、私はそれを働かせることができました。クイックフォローアップ - 私はデフォルトのテンプレートを定義し、次にvsを専門にする必要があるとき混乱しています。私は、デフォルトなしで特殊なバージョンのテンプレートを宣言することができます。 – Ross

+0

@Ross - 回答が改善されました。お役に立てれば。 – max66

0

の@ max66により示唆されるようにあなたがSFINAEを使用することができますが、あなたのユースケースのための簡単な方法はbool過負荷およびテンプレートバージョン

void foo(bool); 

template <class T> 
void foo(T); 

を持っているだろうあなたは(Tintに変換されることを強制することができますstatic_assert)しかし、ほとんどの場合、fooのボディはおそらくこのような場合には不正な形になり、コンパイル時にエラーが発生するため、必要ではありません。あなたの例では

template <class T> 
void foo(T) { 
    static_assert(std::is_convertible<T, int>::value, ""); 
} 

foo(true); // foo(bool) is chosen because it is the best match 
foo((int)5); // foo<int>(int) is chosen, the assertion passes 
foo((unsigned int)10); // foo<unsigned int>(unsigned int) is chosen, assertion ok 
foo((void*)nullptr); // foo<void*>(void*) is chosen, the assertion fails