Cercare di capire i modelli e la ricerca dei nomi


9

Sto cercando di comprendere i seguenti frammenti di codice

Snippet n. 1

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

struct B : A<B>
{
};

Né gcc9 né clang9 generano un errore qui.

D. Perché viene compilato questo codice? Non stiamo istanziando A<B>quando ereditiamo da B? Non c'è VD in B, quindi il compilatore non dovrebbe lanciare un errore qui?

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 questo caso, gcc9 viene compilato correttamente ma clang9 genera un errore che dice "Nessun membro di nome AD in B".

D. Perché si compila con gcc9 / perché non si compila con clang9?

Snippet # 3

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

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

Qui sia clang9 che gcc9 generano un errore. gcc9 dice "uso non valido del tipo incompleto 'struct B'".

D. Se la struttura B è incompleta qui, perché non è incompleta nello snippet # 2?

Compiler bandiere utilizzate: -std=c++17 -O3 -Wall -Werror. Grazie in anticipo!!!


@xception Non è struct Bun'istanza Acon B?
Effetto collaterale mutabile il

clang9 genera un errore che dice "Nessun membro di nome AD in B" . come Bè incompleto ... Ma non sono sicuro di quando un'istanza debba essere istanziata.
Jarod42

@MutableSideEffect oh sì, mia cattiva, leggi anche questo come modello :(
xception

@ Jarod42, allora perché gcc si compila bene?
Effetto collaterale mutabile il

1
Ho segnalato questa domanda come "che necessita di maggiore attenzione" e la domanda contiene effettivamente più di una domanda (quindi la mia conclusione), quindi perché la mia bandiera è sbagliata?
Dominique l'

Risposte:


4

Credo che sostanzialmente si riducano a [temp.inst] / 2 (enfasi sulla mia):

L'istanza implicita di una specializzazione del modello di classe provoca l'istanziazione implicita delle dichiarazioni, ma non delle definizioni , degli argomenti predefiniti o degli identificatori noexcept delle funzioni dei membri della classe, delle classi di membri, delle enumerazioni dei membri con ambito, dei membri di dati statici , dei modelli di membri e amici; [...]

e [temp.inst] / 9

Un'implementazione non deve istanziare implicitamente [...] un membro di dati statici di un modello di classe [...] a meno che tale istanza non sia richiesta.

La formulazione nella norma relativa all'istanza implicita del modello lascia molti dettagli aperti all'interpretazione. In generale, mi sembra che semplicemente non si possa fare affidamento su parti di un modello non vengano istanziate a meno che la specifica non lo dica esplicitamente. Così:

Snippet n. 1

D. Perché viene compilato questo codice? Non stiamo istanziando A quando ereditiamo da B? Non c'è VD in B, quindi il compilatore non dovrebbe lanciare un errore qui?

Stai istanziando A<B>. Ma l'istanza A<B>non fa che istanziare le dichiarazioni, non le definizioni dei suoi membri di dati statici.VBnon viene mai utilizzato in un modo che richiederebbe l'esistenza di una definizione. Il compilatore dovrebbe accettare questo codice.

Snippet # 2

D. Perché si compila con gcc9 / perché non si compila con clang9?

Come sottolineato da Jarod42, la dichiarazione di AB contiene un tipo di segnaposto. Mi sembra che la formulazione dello standard non sia davvero chiara su cosa dovrebbe succedere qui. L'istanza della dichiarazione di un membro di dati statici che contiene un tipo di segnaposto attiva la deduzione del tipo di segnaposto e, quindi, costituisce un uso che richiede la definizione del membro di dati statici? Non riesco a trovare una formulazione nello standard che direbbe chiaramente sì o no. Quindi, direi che entrambe le interpretazioni sono ugualmente valide qui e, quindi, GCC e clang hanno entrambi ragione ...

Snippet # 3

D. Se la struttura B è incompleta qui, perché non è incompleta nello snippet # 2?

Un tipo di classe è solo completo nel punto in cui si raggiunge la chiusura }della classe specificatore [class.mem] / 6 . Pertanto, Bè incompleto durante l'istanza implicita di A<B>in tutti i frammenti. È solo che ciò era irrilevante per lo snippet n. 1. In Snippet # 2, di conseguenza clang ti ha dato un errore No member named AD in B. Simile al caso dello snippet n. 2, non riesco a trovare una formulazione su quando verranno istanziate esattamente le dichiarazioni di alias dei membri. Tuttavia, a differenza della definizione di membri di dati statici, non esiste una formulazione per impedire esplicitamente l'istanza delle dichiarazioni di alias dei membri durante l'istanza implicita di un modello di classe. Quindi, direi che il comportamento di GCC e clang è una valida interpretazione dello standard in questo caso ...


Grazie. In questo caso, inizializziamo il membro di dati statici all'interno del corpo. L'inizializzazione fa parte della dichiarazione o fa parte della definizione del membro di dati statici? Ho avuto l'impressione che se l'inizializzazione è all'interno del corpo, allora fa parte della dichiarazione del membro di dati statici. Se fa parte della dichiarazione, la prima citazione richiede un'istanza immediata come parte dell'istanza implicita circostante del modello di classe.
Johannes Schaub - litb

Ho esaminato le specifiche e sembra che qui ci sia una differenza tra C ++ 14 e C ++ 17. In C ++ 14, il constexprmembro con dati statici era solo una dichiarazione. C ++ 17 ha acquisito inlinevariabili e constexprimplica inline, e ciò rende una definizione del membro dei dati statici nel corpo una definizione.
Johannes Schaub - litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2 dice "Il tipo di una variabile dichiarata usando un tipo di segnaposto viene dedotto dal suo inizializzatore. Questo uso è consentito in una dichiarazione di inizializzazione ([dcl. init]) di una variabile. ". Pertanto, direi che è necessaria la definizione del membro di dati statici, poiché contiene l'inizializzatore.
Johannes Schaub - litb

@ JohannesSchaub-litb Grazie per aver esaminato questo! Mi sono anche posto delle domande su queste domande, ma non sono riuscito a trovare alcuna formulazione conclusiva. Per quanto riguarda l'inizializzazione e la definizione, considera una definizione di una funzione membro all'interno della definizione del modello di classe. Tale definizione è anche una dichiarazione e non ci sono altre dichiarazioni. Tuttavia, l'istanza implicita del modello di classe creerà un'istanza solo di una dichiarazione ma non della definizione della funzione membro. Perché la stessa cosa non sarebbe vera per i membri di dati statici?
Michael Kenzel,

se non c'è nulla che richieda la definizione, la definizione non viene istanziata. Ma nel autocaso, la regola dice che la dichiarazione deve essere una dichiarazione di inizializzazione. Questo può succedere solo se si sa che la dichiarazione è una definizione (per quanto ne so .. Sono stato fuori terra di avvocato per un po '). In passato, c'era un caso simile ed è stata aggiunta eel.is/c++draft/temp.inst#2.sentence-3 , in cui una dichiarazione che è una definizione viene istanziata come "essere noto per essere una definizione" senza effettivamente istanziare la definizione.
Johannes Schaub - litb
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.