Clang está correto ao rejeitar o código no qual a classe aninhada de um modelo de classe é definida apenas por meio de especializações?

17

Dado o seguinte modelo de classe:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

definimos Innerseparadamente para cada especialização de Outer:

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

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

e defina a função de membro fuma vez para todas as especializações de Outer:

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

}

mas Clang (9.0.0) reclama:

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

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

                      ^

Podemos evitar o erro do compilador, fornecendo também uma definição Innerpara todas as outras especializações de Outer:

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

ou definindo fseparadamente para cada especialização:

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

}

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

}

O GCC e o MSVC aceitam o código inicial, o que sugere a pergunta; isso é um bug do Clang ou é a única implementação compatível dos três?

Experimente o Compiler Explorer

invexo
fonte
As especializações do Inner são irrelevantes, removê-las não altera o resultado da compilação.
n. 'pronomes' m.
@ n.'pronouns'm. Não sei bem o que você quer dizer. A adição de uma definição Innerpara todas as outras especializações e a definição fseparada para cada especialização resolvem o erro de compilação.
invexed
Vamos ler novamente: removê-los não altera o resultado da compilação . Não adicionando, removendo. gcc clang
n. 'pronomes' m.
@ n.'pronouns'm. Entendo o que você quer dizer agora, mas ainda é um comentário estranho a fazer. O ponto da minha pergunta é que Innerestá sendo relatado como um tipo incompleto, apesar das definições para cada especialização a Outerser fornecida. Claramente Inner(corretamente) será um tipo incompleto se você remover suas definições.
invexed
"Claramente o Inner (corretamente) será um tipo incompleto se você remover suas definições." Não, isso não está certo. Uma especialização é um modelo completamente separado e não afeta o modelo principal.
n. 'pronomes' m

Respostas:

4

Acredito que Clang esteja errado ao rejeitar seu código. Devemos nos perguntar: como sua declaração e definição de função se comparam às

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

// ...

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

Neste exemplo, T::Inneré obviamente um tipo dependente. Portanto, Clang pode não assumir que está incompleto até a instanciação. O mesmo vale no seu exemplo? Eu diria que sim. Pois temos isso no padrão:

[temp.dep.type]

5 Um nome é um membro da instanciação atual, se for

  • Um nome não qualificado que, quando pesquisado, refere-se a pelo menos um membro de uma classe que é a instanciação atual ou uma classe base não dependente. [Nota: Isso só pode ocorrer ao procurar um nome em um escopo delimitado pela definição de um modelo de classe. - nota final]
  • ...

Um nome é um membro dependente da instanciação atual se for um membro da instanciação atual que, quando consultado, se refere a pelo menos um membro de uma classe que é a instanciação atual.

9 Um tipo é dependente se for

  • ...
  • um membro de uma especialização desconhecida,
  • uma classe ou enumeração aninhada que é um membro dependente da instanciação atual,
  • ...

Portanto, o primeiro item do parágrafo 9 cobre o caso typename T::Inner. Esse é um tipo dependente.

Enquanto isso, seu caso é coberto pela segunda bala. Outer::Inneré um nome encontrado na instanciação atual de Outer, além disso, é encontrado dentro de Outersi mesmo e não em uma classe base. Isso o torna um membro dependente da instanciação atual. Este nome refere-se a uma classe aninhada. O que significa que todas as condições do segundo marcador se aplicam, criando assim Outer::Innerum tipo dependente!

Como temos um tipo dependente nos dois casos, os compiladores devem tratá-los igualmente como tipos dependentes. Minha conclusão é que o GCC e o MSVC estão certos.

Contador de Histórias - Monica Sem Calúnia
fonte
Bug reportado . Obrigado.
invexed