T doit-il être un type complet pour être utilisé dans `std :: declval <T>`?

11

Considérez cet exemple (venant d' ici ):

#include <type_traits>
#include <iostream>
template <typename U>
struct A {
};

struct B {
   template <typename F = int>
   A<F> f() { return A<F>{}; }

   using default_return_type = decltype(std::declval<B>().f());
};

int main()
{
    B::default_return_type x{};
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
}

Il compile sans erreur sur gcc9.2 mais gcc7.2 et clang 10.0.0 se plaignent de Bne pas être complets. L'erreur Clangs est:

prog.cc:11:58: error: member access into incomplete type 'B'
   using default_return_type = decltype(std::declval<B>().f());
                                                         ^
prog.cc:7:8: note: definition of 'B' is not complete until the closing '}'
struct B {
       ^
prog.cc:16:8: error: no type named 'default_return_type' in 'B'
    B::default_return_type x{};
    ~~~^
prog.cc:17:35: error: no member named 'default_return_type' in 'B'
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
                               ~~~^
idclev 463035818
la source
1
Le titre de la question ne semble pas correspondre à l'erreur? Pour moi, il semble que GCC se plaint .f(). Ça a du sens; le type incomplet Bn'a pas de membre f.
MSalters
@MSalters j'ai pensé la même chose, mais alors quel est le vrai problème ici? Je suppose qu'une fois que vous avez obtenu une instance, std::declvalil n'a plus d'importance que le type soit complet ou non (et je suppose que je me trompe)
idclev 463035818
[expr.ref] / 2 (C ++ 11) dit à propos de l'accès des membres de la classe: "Pour la première option (point), la première expression doit avoir le type de classe complet" . Et Bn'est ni complet ni considéré comme complet en alias-declaration.
Language Lawyer
@LanguageLawyer Je n'ai pas trouvé la phrase que vous citez, mais seulement "Le type de classe doit être complet sauf si l'accès des membres de classe apparaît dans la définition de cette classe"
idclev 463035818
1
@LanguageLawyer ok alors je suis d'accord que mon interprétation était éteinte et il semble que quelque chose a changé depuis c ++ 11, ce qui rend les choses ci-dessus correctes dans les normes plus récentes mais pas en c ++ 11. Pourriez-vous écrire une réponse?
idclev 463035818

Réponses:

9

La source de l'erreur n'est pas std::declval, mais l'accès aux membres de classe est incomplet.

Jusqu'à ce que la résolution de CWG1836 soit fusionnée il y a 2,5 ans, la norme exigeait que la classe soit complète dans une expression d'accès de membre de classe ( E1.E2).
[expr.ref] / 2 en C ++ 11 :

Pour la première option (point), la première expression doit avoir un type de classe complet.

[expr.ref] / 2 en C ++ 17 :

Pour la première option (point), la première expression doit être une valeur gl ayant un type de classe complet.

Et une classe n'est pas considérée comme complète en alias-declarationsoi member-specification.
[class.mem] / 6 en C ++ 17 :

Une classe est considérée comme un type d'objet complètement défini ([basic.types]) (ou type complet) à la fermeture }du spécificateur de classe . Dans la spécification de membre de classe , la classe est considérée comme complète dans les corps de fonction, les arguments par défaut, les spécificateurs noexcept et les initialiseurs de membre par défaut (y compris ces éléments dans les classes imbriquées). Sinon, il est considéré comme incomplet dans sa propre classe spécification de membre de .

Avocat en langues
la source
8

De [déclal] :

Remarques: Le paramètre Tde modèle dedeclval peut être un type incomplet.

Cette formulation est présente depuis C ++ 11 (il n'est donc pas possible pour les compilateurs de se conformer à une norme antérieure)

AndyG
la source
génial, c'est ce que j'espérais. On dirait que gcc l'a réparé, pas (encore)
idclev 463035818
@ formerlyknownas_463035818: Ma première pensée a été que ce Tdevrait être absolument un type complet. Heureux d'avoir vérifié la norme.
AndyG