web-dev-qa-db-ja.com

テンプレート引数のenable_if_tが再定義について文句を言うのはなぜですか?

_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の単なるエイリアスです。 gfgの実際の違いがわかるように置き換えてみましょう。

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() { }
10
Oktalist

関数の戻り値の型については、次のものを探しています。

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>();
}

http://ideone.com/TB36gH

も参照

http://ideone.com/EfLkQy

3
kfsone