Dada la siguiente plantilla de clase:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
Definimos Inner
por separado para cada especialización de Outer
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
y luego defina la función miembro f
una 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 Inner
todas las demás especializaciones de Outer
:
template<typename T>
struct Outer<T>::Inner {};
o definiendo por f
separado 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?
Inner
para todas las otras especializaciones como definirf
por separado para cada especialización resuelve el error de compilación.Inner
se informa como un tipo incompleto a pesar de las definiciones para cada especialización queOuter
se 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::Inner
obviamente 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::Inner
es un nombre que se encuentra en la instanciación actual deOuter
, además se encuentra dentro deOuter
sí 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::Inner
un 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