Dada la siguiente plantilla de clase:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
Definimos Innerpor separado para cada especialización de Outer:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
y luego defina la función miembro funa vez para todas las especializaciones de Outer:
auto Outer<T>::f(Inner) -> void
{
}
pero Clang (9.0.0) se queja:
error: variable has incomplete type 'Outer::Inner'
auto Outer<T>::f(Inner) -> void
^
Podemos evadir el error del compilador proporcionando también una definición de Innertodas las demás especializaciones de Outer:
template<typename T>
struct Outer<T>::Inner {};
o definiendo por fseparado para cada especialización:
template<>
auto Outer<int>::f(Inner) -> void
{
}
template<>
auto Outer<double>::f(Inner) -> void
{
}
Tanto GCC como MSVC aceptan el código inicial, lo que plantea la pregunta; ¿Es esto un error de Clang o es la única implementación conforme de las tres?

Innerpara todas las otras especializaciones como definirfpor separado para cada especialización resuelve el error de compilación.Innerse informa como un tipo incompleto a pesar de las definiciones para cada especialización queOuterse proporciona. ClaramenteInner(correctamente) será un tipo incompleto si elimina sus definiciones.Respuestas:
Creo que Clang está mal al rechazar su código. Debemos preguntarnos, ¿cómo se compara su declaración y definición de función con
En este ejemplo,
T::Innerobviamente es un tipo dependiente. Entonces Clang no puede asumir que está incompleto hasta la creación de instancias. ¿Lo mismo es cierto en su ejemplo? Yo diría que sí. Porque tenemos esto en el estándar:Entonces, la primera viñeta en el párrafo 9 cubre el caso
typename T::Inner. Ese es un tipo dependiente.Mientras tanto su caso está cubierto por la segunda viñeta.
Outer::Inneres un nombre que se encuentra en la instanciación actual deOuter, además se encuentra dentro deOutersí mismo, y no en una clase base. Eso lo convierte en un miembro dependiente de la instanciación actual. Este nombre se refiere a una clase anidada. Lo que significa que se aplican todas las condiciones en la segunda viñeta, lo que hace que también seaOuter::Innerun tipo dependiente.Como tenemos un tipo dependiente en ambos casos, los compiladores deben tratarlos igualmente como tipos dependientes. Mi conclusión es que GCC y MSVC tienen razón.
fuente