Der Versuch, Vorlagen und Namenssuche zu verstehen

9

Ich versuche die folgenden Codefragmente zu verstehen

Snippet # 1

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

struct B : A<B>
{
};

Weder gcc9 noch clang9 werfen hier einen Fehler.

F. Warum wird dieser Code kompiliert? Instanziieren wir nicht, A<B>wenn wir von B erben? Es gibt kein VD in B, sollte der Compiler hier also keinen Fehler auslösen?

Snippet # 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;
};

In diesem Fall wird gcc9 gut kompiliert, aber clang9 gibt den Fehler "Kein Mitglied mit dem Namen AD in B" aus.

F. Warum wird es mit gcc9 kompiliert / warum wird es nicht mit clang9 kompiliert?

Snippet # 3

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

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

Hier werfen sowohl clang9 als auch gcc9 einen Fehler. gcc9 sagt "ungültige Verwendung des unvollständigen Typs 'Struktur B'".

F. Wenn Struktur B hier unvollständig ist, warum ist sie dann in Snippet 2 nicht unvollständig?

Verwendete Compiler-Flags : -std=c++17 -O3 -Wall -Werror. Danke im Voraus!!!

Mutable Nebenwirkung
quelle
@xception struct BInstanziiert nicht Amit B?
Mutable Nebenwirkung
clang9 gibt den Fehler "Kein Mitglied mit dem Namen AD in B" aus . wie Bist unvollständig ... Aber unsicher, wann Mitglied instanziiert werden soll ..
Jarod42
@MutableSideEffect oh ja, mein schlechtes, lesen Sie das auch als Vorlage :(
xception
@ Jarod42 warum kompiliert gcc dann gut?
Mutable Nebenwirkung
1
Ich habe diese Frage als "mehr Fokus benötigen" gekennzeichnet und die Frage enthält tatsächlich mehr als eine Frage (daher meine Schlussfolgerung). Warum ist meine Flagge also falsch?
Dominique

Antworten:

4

Ich glaube, diese beschränken sich im Wesentlichen auf [temp.inst] / 2 (Hervorhebung von mir):

Die implizite Instanziierung einer Klassenvorlagenspezialisierung bewirkt die implizite Instanziierung der Deklarationen, jedoch nicht der Definitionen , Standardargumente oder Noexcept-Spezifizierer der Klassenelementfunktionen, Elementklassen, Elementaufzählungen mit Gültigkeitsbereich, statischen Datenelemente , Elementvorlagen und Freunde; […]

und [temp.inst] / 9

Eine Implementierung darf ein statisches Datenelement einer Klassenvorlage […] nicht implizit instanziieren […], es sei denn, eine solche Instanziierung ist erforderlich.

Der Wortlaut des Standards zur impliziten Instanziierung von Vorlagen lässt viele Details für die Interpretation offen. Im Allgemeinen scheint es mir, dass Sie sich einfach nicht darauf verlassen können, dass Teile einer Vorlage nicht instanziiert werden, es sei denn, die Spezifikation sagt dies ausdrücklich aus. Somit:

Snippet # 1

F. Warum wird dieser Code kompiliert? Instanziieren wir nicht A, wenn wir von B erben? Es gibt kein VD in B, sollte der Compiler hier also keinen Fehler auslösen?

Sie instanziieren A<B>. Durch das Instanziieren werden jedoch A<B>nur die Deklarationen instanziiert, nicht die Definitionen der statischen Datenelemente. VBwird niemals so verwendet, dass eine Definition erforderlich wäre. Der Compiler sollte diesen Code akzeptieren.

Snippet # 2

F. Warum wird es mit gcc9 kompiliert / warum wird es nicht mit clang9 kompiliert?

Wie von Jarod42 hervorgehoben, ist die Erklärung von AB enthält einen Platzhaltertyp. Es scheint mir, dass der Wortlaut des Standards nicht wirklich klar ist, was hier passieren soll. Löst die Instanziierung der Deklaration eines statischen Datenelements, das einen Platzhaltertyp enthält, einen Platzhaltertypabzug aus und stellt somit eine Verwendung dar, die die Definition des statischen Datenelements erfordert? Ich kann in der Norm keinen Wortlaut finden, der eindeutig Ja oder Nein dazu sagt. Daher würde ich sagen, dass beide Interpretationen hier gleichermaßen gültig sind und somit sowohl GCC als auch Clang richtig sind…

Snippet # 3

F. Wenn Struktur B hier unvollständig ist, warum ist sie dann in Snippet 2 nicht unvollständig?

Ein Klassentyp ist erst an dem Punkt vollständig, an dem Sie das Schließen }des Klassenspezifizierers [class.mem] / 6 erreichen . Somit Bist während der impliziten Instanziierung A<B>in all Ihren Snippets unvollständig . Es ist nur so, dass dies für Snippet # 1 irrelevant war. In Snippet # 2 hat clang No member named AD in Bals Ergebnis einen Fehler ausgegeben . Ähnlich wie im Fall von Snippet Nr. 2 kann ich keine Formulierung finden, wann genau die Alias-Deklarationen der Mitglieder instanziiert würden. Anders als bei der Definition statischer Datenelemente gibt es jedoch keinen Wortlaut, der die Instanziierung von Elementaliasdeklarationen während der impliziten Instanziierung einer Klassenvorlage explizit verhindert. Daher würde ich sagen, dass das Verhalten von GCC und Clang in diesem Fall eine gültige Interpretation des Standards ist…

Michael Kenzel
quelle
Vielen Dank. In diesem Fall initialisieren wir das statische Datenelement innerhalb des Körpers. Ist die Initialisierung Teil der Deklaration oder Teil der Definition des statischen Datenelements? Ich hatte den Eindruck, dass wenn die Initialisierung innerhalb des Körpers ist, dies Teil der Deklaration des statischen Datenelements ist. Wenn es Teil der Deklaration ist, erfordert Ihr erstes Zitat eine sofortige Instanziierung als Teil der umgebenden impliziten Instanziierung der Klassenvorlage.
Johannes Schaub - Litb
Ich habe mir die Spezifikation angesehen und es scheint, dass es hier einen Unterschied zwischen C ++ 14 und C ++ 17 gibt. In C ++ 14 war das constexprstatische Datenelement nur eine Deklaration. C ++ 17 hat inlineVariablen gewonnen und constexprimpliziert inline, und dies macht die Deklaration statischer Datenelemente im Hauptteil zu einer Definition.
Johannes Schaub - Litb
eel.is/c++draft/dcl.spec.auto#4.sentence-2 sagt: "Der Typ einer Variablen, die mit einem Platzhaltertyp deklariert wurde, wird von ihrem Initialisierer abgeleitet. Diese Verwendung ist in einer Initialisierungsdeklaration zulässig ([dcl. init]) einer Variablen. ". Daher würde ich argumentieren, dass die Definition des statischen Datenelements erforderlich ist, da es den Initialisierer enthält.
Johannes Schaub - litb
@ JohannesSchaub-litb Danke, dass du dir das angeschaut hast! Ich habe mich auch über diese Fragen gewundert, konnte aber keinen schlüssigen Wortlaut finden. Berücksichtigen Sie in Bezug auf die Initialisierung vs. Definition eine Definition einer Elementfunktion in der Definition der Klassenvorlage. Eine solche Definition ist auch eine Erklärung, und es gibt keine anderen Erklärungen. Die implizite Instanziierung der Klassenvorlage instanziiert jedoch nur eine Deklaration, nicht jedoch die Definition der Elementfunktion. Warum sollte das Gleiche nicht für statische Datenelemente gelten?
Michael Kenzel
Wenn es nichts gibt, das die Definition erfordert, wird die Definition nicht instanziiert. In diesem autoFall besagt die Regel jedoch, dass die Deklaration eine Initialisierungsdeklaration sein muss. Dies kann nur der Fall sein, wenn bekannt ist, dass es sich bei der Erklärung um eine Definition handelt (soweit ich weiß. Ich war eine Weile außerhalb des Anwaltslandes). In der Vergangenheit gab es einen ähnlichen Fall und eel.is/c++draft/temp.inst#2.sentence-3 wurde hinzugefügt, wobei eine Deklaration, die eine Definition ist, als "bekannt als Definition" instanziiert wird, ohne tatsächlich Instanziieren der Definition.
Johannes Schaub - Litb