Essayer de comprendre les modèles et la recherche de nom

9

J'essaie de comprendre les extraits de code suivants

Extrait # 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

Ni gcc9 ni clang9 ne génèrent d'erreur ici.

Q. Pourquoi ce code est-il compilé? N'instancions-nous pas A<B>en héritant de B? Il n'y a pas de VD dans B, donc le compilateur ne devrait-il pas lancer une erreur ici?

Extrait # 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

Dans ce cas, gcc9 compile bien mais clang9 renvoie une erreur disant "Aucun membre nommé AD dans B".

Q. Pourquoi compile-t-il avec gcc9 / pourquoi ne compile-t-il pas avec clang9?

Extrait # 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

Ici, clang9 et gcc9 génèrent une erreur. gcc9 indique "utilisation non valide de type incomplet 'struct B'".

Q. Si la structure B est incomplète ici, pourquoi ne l'est-elle pas dans l'extrait de code # 2?

Drapeaux compilateur utilisé: -std=c++17 -O3 -Wall -Werror. Merci d'avance!!!

Effet secondaire mutable
la source
@xception est -ce pas l' struct Binstanciation Aavec B?
Mutable Side Effect
clang9 renvoie une erreur indiquant "Aucun membre nommé AD dans B" . car il Best incomplet ... Mais
je ne sais
@MutableSideEffect oh oui, mon mauvais, lisez-le également comme modèle :(
xception
@ Jarod42 alors pourquoi gcc se compile bien?
Mutable Side Effect
1
J'ai signalé cette question comme "nécessitant plus de concentration" et la question contient en effet plus d'une question (d'où ma conclusion), alors pourquoi mon drapeau est-il faux?
Dominique

Réponses:

4

Je crois que cela se résume essentiellement à [temp.inst] / 2 (c'est moi qui souligne):

L'instanciation implicite d'une spécialisation de modèle de classe provoque l'instanciation implicite des déclarations, mais pas des définitions , des arguments par défaut ou des spécificateurs noexcept des fonctions membres de classe, classes membres, énumérations de membres étendues, membres de données statiques , modèles de membres et copains; […]

et [temp.inst] / 9

Une implémentation ne doit pas implicitement instancier […] un membre de données statique d'un modèle de classe […] sauf si une telle instanciation est requise.

Le libellé de la norme concernant l'instanciation implicite de modèles laisse de nombreux détails ouverts à l'interprétation. En général, il me semble que vous ne pouvez tout simplement pas compter sur des parties d'un modèle qui ne sont pas instanciées à moins que la spécification ne le dise explicitement. Donc:

Extrait # 1

Q. Pourquoi ce code est-il compilé? N'instancions-nous pas A en héritant de B? Il n'y a pas de VD dans B, donc le compilateur ne devrait-il pas lancer une erreur ici?

Vous instanciez A<B>. Mais l'instanciation instancie A<B>uniquement les déclarations, pas les définitions de ses membres de données statiques. VBn'est jamais utilisé d'une manière qui nécessiterait une définition. Le compilateur doit accepter ce code.

Extrait # 2

Q. Pourquoi compile-t-il avec gcc9 / pourquoi ne compile-t-il pas avec clang9?

Comme l'a souligné Jarod42, la déclaration de ABcontient un type d'espace réservé. Il me semble que le libellé de la norme n'est pas vraiment clair sur ce qui est censé se produire ici. L'instanciation de la déclaration d'un membre de données statique qui contient un type d'espace réservé déclenche-t-elle une déduction de type d'espace réservé et, par conséquent, constitue-t-elle une utilisation qui nécessite la définition du membre de données statique? Je ne trouve pas de libellé dans la norme qui dirait clairement oui ou non. Ainsi, je dirais que les deux interprétations sont également valables ici et, par conséquent, GCC et clang ont tous deux raison…

Extrait # 3

Q. Si la structure B est incomplète ici, pourquoi ne l'est-elle pas dans l'extrait de code # 2?

Un type de classe n'est complet qu'au point où vous atteignez la fermeture }du spécificateur de classe [class.mem] / 6 . Ainsi, Best incomplet lors de l'instanciation implicite de A<B>dans tous vos extraits. C'est juste que cela n'était pas pertinent pour l'extrait de code n ° 1. Dans l'extrait de code n ° 2, clang vous a donné une erreur No member named AD in Ben conséquence. Semblable au cas de l'extrait de code # 2, je ne trouve pas de libellé sur le moment exact où les déclarations d'alias de membre seraient instanciées. Cependant, contrairement à la définition des membres de données statiques, aucun libellé n'est en place pour empêcher explicitement l'instanciation des déclarations d'alias de membre lors de l'instanciation implicite d'un modèle de classe. Ainsi, je dirais que le comportement de GCC et de clang est une interprétation valide de la norme dans ce cas…

Michael Kenzel
la source
Merci. Dans ce cas, nous initialisons le membre de données statiques dans le corps. L'initialisation fait-elle partie de la déclaration ou fait-elle partie de la définition du membre de données statique? J'avais l'impression que si l'initialisation se trouve dans le corps, cela fait partie de la déclaration du membre de données statiques. Si cela fait partie de la déclaration, votre première citation nécessite une instanciation immédiate dans le cadre de l'instanciation implicite environnante du modèle de classe.
Johannes Schaub - litb
J'ai examiné la spécification et il semble qu'il y ait une différence entre C ++ 14 et C ++ 17 ici. En C ++ 14, le constexprmembre de données statiques n'était qu'une déclaration. C ++ 17 a gagné des inlinevariables et constexprimplique inline, ce qui fait de la déclaration de membre de données statiques internes une définition.
Johannes Schaub - litb
eel.is/c++draft/dcl.spec.auto#4.sentence-2 dit "Le type d'une variable déclarée à l'aide d'un type d'espace réservé est déduit de son initialiseur. Cette utilisation est autorisée dans une déclaration d'initialisation ([dcl. init]) d'une variable. ". Par conséquent, je dirais que la définition du membre de données statiques est requise, car elle contient l'initialiseur.
Johannes Schaub - litb
@ JohannesSchaub-litb Merci d'avoir étudié cela! Je me posais également des questions sur ces questions, mais je n'ai trouvé aucun libellé qui serait concluant. Concernant l'initialisation vs la définition, considérez une définition d'une fonction membre à l'intérieur de la définition du modèle de classe. Une telle définition est également une déclaration et il n'y a pas d'autres déclarations. Pourtant, l'instanciation implicite du modèle de classe ne fera qu'instancier une déclaration mais pas la définition de la fonction membre. Pourquoi la même chose ne serait-elle pas vraie pour les membres de données statiques?
Michael Kenzel
s'il n'y a rien qui nécessite la définition, la définition n'est pas instanciée. Mais dans le autocas, la règle dit que la déclaration doit être une déclaration d'initialisation. Cela ne peut être le cas que si l'on sait que la déclaration est une définition (pour autant que je sache ... je suis hors du pays des avocats depuis un certain temps). Dans le passé, il y avait un cas similaire et eel.is/c++draft/temp.inst#2.sentence-3 a été ajouté, où une déclaration qui est une définition est instanciée comme "étant connue pour être une définition" sans réellement instancier la définition.
Johannes Schaub - litb