web-dev-qa-db-fra.com

Pourquoi enable_if_t dans les arguments de modèle se plaint-il des redéfinitions?

J'ai le cas suivant qui fonctionne en utilisant 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() { }

Maintenant, j'ai vu dans cppreference la nouvelle syntaxe, beaucoup plus propre à mon avis: typename = std::enable_if_t<std::is_same<int, T>::value>>

Je voulais porter mon code:

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

Mais maintenant, GCC (5.2) se plaint:

error: redefinition of 'template<class T, class> void g()'
       void g() { }

Pourquoi est-ce si ? Que puis-je faire pour avoir la nouvelle syntaxe plus concise dans ce cas si cela est possible?

35

Supprimons du code.

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

seriez-vous surpris si le compilateur rejetait les deux modèles ci-dessus?

Ce sont deux fonctions modèles de "type" template<class,class>void(). Le fait que le deuxième type d'argument ait une valeur par défaut différente n'a pas d'importance. Ce serait comme s’attendre à ce que deux fonctions print(string, int) différentes avec des valeurs par défaut int différentes soient surchargées. ;)

Dans le premier cas, nous avons:

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

ici, nous ne pouvons pas supprimer la clause enable_if. Mise à jour vers 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() { }

J'ai également remplacé une utilisation de typename par class. Je soupçonne que votre confusion était due au fait que typename a deux significations - une en tant que marqueur pour une sorte d'argument template et une autre en tant que désambiguïsateur pour un type dépendant.

Ici, le deuxième argument est un pointeur, dont le type dépend du premier. Le compilateur ne peut pas déterminer si ces deux conflits sans d'abord les remplacer par le type T - et vous remarquerez qu'ils ne seront jamais réellement en conflit.

41

enable_if_t<B> n'est qu'un alias pour typename enable_if<B>::type. Remplaçons cela dans g afin que nous puissions voir la vraie différence entre f et 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() { }

Dans le cas de f, nous avons deux modèles de fonction avec les paramètres de modèle <typename, X*>, où le type X est dépendant du type du premier modèle argument. Dans le cas de g nous avons deux modèles de fonction avec des paramètres de modèle <typename, typename> et ce n'est que l'argument de modèle par défaut qui est dépendant, donc C++ considère qu'ils déclarent tous les deux la même entité.

Les deux styles peuvent être utilisés avec le enable_if_t alias:

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

Pour un type de retour de fonction, vous recherchez les éléments suivants:

template<typename T> std::enable_if_t< conditional, instantiation result > foo();

Exemple:

#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

voir également

http://ideone.com/EfLkQy

3
kfsone