2017-10-05 4 views
1

a couru dans une autre bizarrerie dans les guides de déduction de modèle après C++17 template deduction guide not used for empty parameter set? (ce bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81486 est toujours pas fixé dans le coffre GCC malheureusement :():Guide de déduction de gabarit C++ 17 non utilisé pour le jeu de paramètres vide (ver 2)?

#include <utility> 

template <class T> struct success 
{ 
    T value; 
    constexpr success(T &&v) 
     : value(std::move(v)) 
    { 
    } 
    constexpr success(const T &v) 
     : value(v) 
    { 
    } 
}; 
template <> struct success<void> 
{ 
}; 
template <class T> success(T /*unused*/)->success<T>; 
success()->success<void>; 

template<class T> struct foo 
{ 
    foo(success<void>) {} 
}; 

int main(void) 
{ 
    auto a = success{5};  // works 
    auto b = success{};   // works 
    auto c = success{"hello"}; // works 
    auto d = success(5);  // works 
    //auto e = success();   // FAILS on GCC 7.2! 
    auto f = success("hello"); // works 
    foo<void> g(success());  // FAILS 
    static_assert(std::is_same<decltype(a), success<int>>::value, ""); 
    static_assert(std::is_same<decltype(b), success<void>>::value, ""); 
    static_assert(std::is_same<decltype(c), success<const char *>>::value, ""); 
    static_assert(std::is_same<decltype(d), success<int>>::value, ""); 
    //static_assert(std::is_same<decltype(e), success<void>>::value, ""); 
    static_assert(std::is_same<decltype(f), success<const char *>>::value, ""); 
    return 0; 
} 

La ligne surprenante, du moins pour moi, est que foo<void> g(success()); ne parvient pas à utiliser les guides de déduction de modèle sur les deux clang 6.0 tronc et le tronc GCC 7 que vous pouvez voir à https://godbolt.org/g/7m1Zhk

Je trouve cela surprenant et non ce que l'on pouvait s'y attendre. Le guide de modèle dit qu'un sans fioritures success() doit être construit à success<void>. cela devrait fonctionne très bien avec unambi de foo constructeur Guous acceptant un success<void>. Au lieu de cela clang 6.0 rapports du tronc:

34 : <source>:34:17: error: use of class template 'success' requires template arguments; argument deduction not allowed in function return type 
    foo<void> g(success());  // FAILS 
       ^~~~~~~ 
3 : <source>:3:27: note: template is declared here 
template <class T> struct success 
         ^

et GCC 7.3 Rapports du tronc:

<source>: In function 'int main()': 
34 : <source>:34:25: error: 'auto' parameter not permitted in this context 
    foo<void> g(success());  // FAILS 
         ^

Quelqu'un peut-il expliquer ce qui se passe ici? Est-ce un défaut de la norme C++ 17?

Répondre

4

Je crois que vous avez rencontré une nouvelle forme de l'analyse la plus vexante. Rappelez-vous que la forme syntaxique de n'importe quel morceau de code est déterminée avant que les règles sémantiques au-delà de la recherche de nom ne s'y appliquent. Maintenant que le nom de modèle- est syntaxiquement valide simple type spécificateur, il devient possible d'analyser

foo<void> g(success()); 

que ce soit une définition d'un objet g avec initialiseur ou une déclaration d'une fonction g. Selon la règle Parsing Most Vexing, la fonction parse "wins", donc g déclare une fonction retournant foo<void> dont un paramètre sans nom est une fonction sans paramètre retournant le type de modèle de classe de placeholder success. Cependant, lorsque les vérifications sémantiques sont exécutées, ce n'est pas l'une des utilisations valides d'un type d'espace réservé de modèle de classe, donc le programme est mal formé.

Remarque clang réussira si nous faisons quelques modifications pour éviter le plus contrariant Parse:

foo<void> g2{success()}; 
struct bar { bar(int, succeed<void>) {} }; 
bar g3(1, success()); 

Cependant, je pense que l'astuce double parenthèses suivante devrait également fonctionner, mais il provoque simplement de nouveaux messages d'erreur de tintement. Je ne suis pas sûr de ce qui se passe avec celui-ci:

foo<void> g4((success())); 
+0

Ce message clang est en fait [bug 34091] (http://llvm.org/pr34091) – Rakete1111

+0

Ok, merci pour la confirmation de la cause, il était difficile de choisir entre ça et l'autre réponse , les deux sont bons, mais celui-ci a aussi une solution de contournement donc je le pense mieux. –

2

C'est l'analyse la plus vexante.

foo<void> g(success());  // FAILS 

La déclaration d'une fonction, appelée g, qui retourne une foo<void> et prend comme un paramètre de type pointeur à la fonction arité [sans nom] retour success.

Cependant, success n'est pas un type, c'est un nom de modèle, et vous ne pouvez pas utiliser un nom de modèle comme type de retour d'une fonction, seulement un type complet. Par conséquent, l'erreur.

+0

nom de modèle, pas d'ID de modèle. –

+0

@ T.C. Vive, template-id est ce que vous voulez réellement ici n'est-ce pas ... – Barry

+0

"Cependant, le succès n'est pas un type, c'est un template-name, et vous ne pouvez pas utiliser un template-name comme type de retour d'une fonction, seulement un type complet, d'où l'erreur. " Je vois le problème maintenant. Une solution de contournement pour un futur standard est que si le type de modèle par défaut tous ses arguments de modèle, cette valeur par défaut peut être injectée dans l'espace de type ainsi que l'espace de modèle. Oui, je suis d'accord que c'est un cas d'angle terrible, mais le problème d'analyse ennuyeux m'empêche de remplacer un ensemble de fonctions libres et de types de cales avec un système de guide de déduction de gabarit plus propre. Ce qui est triste :( –