_std::enable_if
_を使用して機能する次のケースがあります:
_template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
_
今、私はcppreferenceで新しい構文を見ました、私の意見でははるかにきれいです:_typename = std::enable_if_t<std::is_same<int, T>::value>>
_
私のコードを移植したかった:
_template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }
_
しかし今、GCC(5.2)は不平を言っています:
_error: redefinition of 'template<class T, class> void g()'
void g() { }
_
どうしてこんなことに ?これが可能であれば、この場合、新しいより簡潔な構文を使用するにはどうすればよいですか?
いくつかのコードを削除しましょう。
_template<
class T,
class U/* = std::enable_if_t<std::is_same<int, T>::value>*/
>
void g() { }
template<
class T,
class U/* = std::enable_if_t<std::is_same<double, T>::value>*/
>
void g() { }
_
コンパイラが上記の2つのテンプレートを拒否した場合、あなたは驚きますか?
どちらも「タイプ」のテンプレート関数template<class,class>void()
です。 2番目の型の引数が異なるdefault値を持つという事実は重要ではありません。それは、異なるデフォルトのint
値を持つ2つの異なるprint(string, int)
関数がオーバーロードすることを期待するようなものです。 ;)
最初のケースでは、次のようになります。
_template<
typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr
>
void f() { }
template<
typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr
>
void f() { }
_
ここでは、_enable_if
_句を削除できません。 _enable_if_t
_に更新しています:
_template<
class T,
std::enable_if_t<std::is_same<int, T>::value, int>* = nullptr
>
void f() { }
template<
class T,
std::enable_if_t<std::is_same<double, T>::value, int>* = nullptr
>
void f() { }
_
また、typename
の使用をclass
に置き換えました。 typename
には2つの意味があります。1つはある種のtemplate
引数のマーカーとして、もう1つは依存型の曖昧性解消子としてです。
ここで、2番目の引数はポインターであり、その型は最初の引数に依存しています。コンパイラは、最初にタイプT
を代入しないと、これら2つが競合するかどうかを判別できません。実際に競合することはありません。
enable_if_t<B>
はtypename enable_if<B>::type
の単なるエイリアスです。 g
をf
とg
の実際の違いがわかるように置き換えてみましょう。
template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename = typename std::enable_if<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = typename std::enable_if<std::is_same<double, T>::value>::type>
void g() { }
f
の場合、2つの関数テンプレートがあり、どちらもテンプレートパラメーター<typename, X*>
を使用しています。ここで、タイプX
は、最初のタイプの依存です。テンプレート引数。 g
の場合、テンプレートパラメータ<typename, typename>
を持つ2つの関数テンプレートがあり、依存するのはデフォルトのテンプレート引数のみであるため、C++は両方が同じエンティティを宣言していると見なします。
どちらのスタイルもenable_if_t
エイリアスで使用できます。
template<typename T,
std::enable_if_t<std::is_same<int, T>::value>* = nullptr>
void f() { }
template<typename T,
std::enable_if_t<std::is_same<double, T>::value>* = nullptr>
void f() { }
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }
関数の戻り値の型については、次のものを探しています。
template<typename T> std::enable_if_t< conditional, instantiation result > foo();
例:
#include <iostream>
// when T is "int", replace with 'void foo()'
template<typename T>
std::enable_if_t<std::is_same<int, T>::value, void> foo() {
std::cout << "foo int\n";
}
template<typename T>
std::enable_if_t<std::is_same<float, T>::value, void> foo() {
std::cout << "foo float\n";
}
int main() {
foo<int>();
foo<float>();
}
も参照