Dado o seguinte modelo de classe:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
definimos Inner
separadamente para cada especialização de Outer
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
e defina a função de membro f
uma 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 Inner
para todas as outras especializações de Outer
:
template<typename T>
struct Outer<T>::Inner {};
ou definindo f
separadamente 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?
Inner
para todas as outras especializações e a definiçãof
separada para cada especialização resolvem o erro de compilação.Inner
está sendo relatado como um tipo incompleto, apesar das definições para cada especialização aOuter
ser fornecida. ClaramenteInner
(corretamente) será um tipo incompleto se você remover suas definições.Respostas:
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
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: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 deOuter
, além disso, é encontrado dentro deOuter
si 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 assimOuter::Inner
um 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.
fonte