Clang ha ragione a rifiutare il codice in cui la classe nidificata di un modello di classe è definita solo tramite specializzazioni?


17

Dato il seguente modello di classe:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

definiamo Innerseparatamente per ogni specializzazione di Outer:

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};

e quindi definire funa volta la funzione membro per tutte le specializzazioni di Outer:

auto Outer<T>::f(Inner) -> void
{

}

ma Clang (9.0.0) si lamenta:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^

Possiamo eludere l'errore del compilatore fornendo anche una definizione di Innerper tutte le altre specializzazioni di Outer:

template<typename T>
struct Outer<T>::Inner {};

o definendo fseparatamente per ogni specializzazione:

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}

Sia GCC che MSVC accettano il codice iniziale, che pone la domanda; si tratta di un bug di Clang o è l'unica implementazione conforme delle tre?

Prova su Compiler Explorer


Le specializzazioni di Inner sono irrilevanti, rimuoverle non modifica il risultato della compilazione.
n. 'pronomi' m.

@ n.'pronouns'm. Non sono sicuro di cosa intendi. Sia l' aggiunta di una definizione di Innerper tutte le altre specializzazioni sia la definizione fseparata per ciascuna specializzazione risolvono l'errore di compilazione.
Invexed il

Leggiamolo di nuovo: rimuoverli non modifica il risultato della compilazione . Non aggiungere, rimuovere. gcc clang
n. 'pronomi' m.

@ n.'pronouns'm. Capisco cosa intendi ora, ma è ancora uno strano commento da fare. Il punto della mia domanda era che Innerveniva segnalato come un tipo incompleto nonostante le definizioni per ogni specializzazione di Outeressere fornita. Chiaramente Innersarà (correttamente) un tipo incompleto se si rimuovono le sue definizioni.
Invexato il

"Chiaramente Inner sarà (correttamente) un tipo incompleto se si rimuovono le sue definizioni." No, quello non è affatto evidente. Una specializzazione è un modello completamente separato e non influenza affatto il modello principale.
n. 'pronomi'

Risposte:


4

Credo che Clang abbia sbagliato a rifiutare il tuo codice. Dobbiamo chiederci, come si confronta la tua dichiarazione di funzione e definizione

auto f(typename T::Inner) -> void;

// ...

template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }

In questo esempio, T::Innerè ovviamente un tipo dipendente. Quindi Clang non può supporre che sia incompleto fino all'istanza. Lo stesso vale nel tuo esempio? Direi di si. Perché abbiamo questo nello standard:

[Temp.dep.type]

5 Un nome è un membro dell'istanza corrente, se lo è

  • Un nome non qualificato che, quando cercato, si riferisce ad almeno un membro di una classe che è l'istanza corrente o una sua classe base non dipendente. [Nota: ciò può verificarsi solo quando si cerca un nome in un ambito racchiuso dalla definizione di un modello di classe. - nota finale]
  • ...

Un nome è un membro dipendente dell'istanza corrente se è un membro dell'istanza corrente che, quando cercato, fa riferimento ad almeno un membro di una classe che è l'istanza corrente.

9 Un tipo dipende se lo è

  • ...
  • un membro di una specializzazione sconosciuta,
  • una classe o enumerazione nidificata che è un membro dipendente dell'istanza corrente,
  • ...

Quindi il primo punto nel paragrafo 9 copre il caso typename T::Inner. Questo è un tipo dipendente.

Nel frattempo il tuo caso è coperto dal secondo proiettile. Outer::Innerè un nome che si trova nell'istanza corrente di Outer, inoltre si trova dentro Outerse stesso e non in una classe base. Ciò lo rende un membro dipendente dell'istanza corrente. Questo nome si riferisce a una classe nidificata. Il che significa che si applicano tutte le condizioni nel secondo punto, rendendo anche Outer::Innerun tipo dipendente!

Poiché in entrambi i casi abbiamo un tipo dipendente, i compilatori dovrebbero trattarli allo stesso modo come tipi dipendenti. La mia conclusione è che GCC e MSVC hanno ragione.


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.