¿Es correcto Clang para rechazar el código en el que la clase anidada de una plantilla de clase se define solo a través de especializaciones?

17

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?

Probar en el Explorador de compiladores

invexed
fuente
Las especializaciones de Inner son irrelevantes, eliminarlas no cambia el resultado de la compilación.
n. 'pronombres' m.
@ n.'pronouns'm. No estoy seguro de lo que quieres decir. Tanto agregar una definición Innerpara todas las otras especializaciones como definir fpor separado para cada especialización resuelve el error de compilación.
invexed el
Leamos de nuevo: eliminarlos no cambia el resultado de la compilación . No agregar, quitar. gcc clang
n. 'pronombres' m.
@ n.'pronouns'm. Ahora entiendo a qué te refieres, pero sigue siendo un comentario extraño. El punto de mi pregunta fue que Innerse informa como un tipo incompleto a pesar de las definiciones para cada especialización que Outerse proporciona. Claramente Inner(correctamente) será un tipo incompleto si elimina sus definiciones.
invexed el
"Claramente interior será (correctamente) sea un tipo incompleto si se quita su definición (s)." Sin que "no es en absoluto ckear Una especialización es una plantilla completamente separados y que no afecta a la plantilla principal en absoluto..
n. 'pronombres' m.

Respuestas:

4

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

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

// ...

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

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:

[temp.dep.type]

5 Un nombre es un miembro de la instanciación actual si es

  • Un nombre no calificado que, cuando se busca, se refiere al menos a un miembro de una clase que es la instanciación actual o una clase base no dependiente de la misma. [Nota: Esto solo puede ocurrir cuando se busca un nombre en un ámbito incluido en la definición de una plantilla de clase. - nota final]
  • ...

Un nombre es un miembro dependiente de la instancia actual si es un miembro de la instancia actual que, cuando se busca, se refiere al menos a un miembro de una clase que es la instancia actual.

9 9 Un tipo es dependiente si es

  • ...
  • un miembro de una especialización desconocida,
  • una clase o enumeración anidada que es un miembro dependiente de la instanciación actual,
  • ...

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 de Outer, además se encuentra dentro de Outersí 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 sea Outer::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.

StoryTeller - Unslander Monica
fuente
Error reportado . Gracias.
invexed