J’ai perdu d’innombrables heures pour identifier un problème avec gcc . Je voulais tester notre base de code avec un autre compilateur pour rechercher plus d’avertissements que Clang aurait pu manquer. J’ai été choqué que pratiquement la moitié du projet se soit arrêté en raison de l’échec de la déduction des arguments du modèle. Ici, j’ai essayé de réduire mon cas au code le plus simple.
#include struct Foo { }; // This is a template function declaration, where second template argument declared without a default template void foo(const Foo & foo, T t); // This is a template function definition; second template argument now has a default declared template <typename T, typename = typename std::enable_if::type> void foo(const Foo & foo, T t) { } int main(int argc, char ** argv) { foo(Foo{}, 1); return 0; }
Ignorer un 1
dans le std::enable_if
. De toute évidence, il suffit de ne pas compliquer les choses, peu importe.
Ce morceau de code comstack [1] avec clang (3.4 à 4.0), icc (16, 17), Visual C ++ (19.00.23506). Fondamentalement, je n’ai trouvé aucun autre compilateur c ++ 11 qui, à l’exception de gcc (4.8 à 7.1), ne comstack pas ce morceau de code.
La question est: qui a raison et qui a tort ici? Est-ce que gcc se comporte selon la norme?
Évidemment, ce n’est pas un problème critique. Je peux facilement déplacer std::enable_if
vers la déclaration. La seule victime serait l’esthétique. Mais il est bon de pouvoir cacher un morceau de code std::enable_if
long de 100 caractères, ce qui n’est pas immédiatement pertinent pour l’utilisateur de la fonction bibliothèque, dans l’implémentation.
Exemple en direct sur godbolt.org .
Que dit la norme ( [1] page 350):
L’ensemble des arguments de template par défaut disponibles pour une déclaration ou une définition de modèle est obtenu en fusionnant les arguments par défaut de la définition (si dans la scope) et toutes les déclarations de la même manière que les arguments de fonction par défaut (8.3.6). [ Exemple:
template
class A; template class A; is equivalent to template class A; – exemple de fin]
Donc, GCC a tort ici. Il ignore les arguments de modèle par défaut dans les déclarations.
Pas toutes les déclarations, seulement les déclarations de modèles de fonction. Les déclarations de modèle de classe sont correctes:
#include template struct Foo; template ::type> struct Foo { T t; }; int main() { Foo foo; return 0; }
Exemple en direct sur godbolt.org
Cela est probablement dû à la nature de la manière dont les arguments autres que par défaut sont déduits. Dans le modèle de fonction, ils sont déduits des arguments de fonction. Dans le modèle de classe, nous devons les spécifier explicitement.
Quoi qu’il en soit, j’ai créé un rapport de bogue .