Pregunta sobre c++, clang++, g++, templates, forward-declaration – La declaración de clase de reenvío utilizada en la función de plantilla no está compilada por clang ++

12

Hay este código:

class A;

template <class T>
void fun() {
   A a;
}

class A { 
public: 
   A() {  } 
};

int main() { 
   fun<int>(); 
   return 0;
}

g ++ 4.5 y g ++ 4.7 compilan esto sin error. Pero clang ++ 3.2 (tronco) da este error:

main.cpp:5:6: error: variable has incomplete type 'A'
   A a;
     ^
main.cpp:1:7: note: forward declaration of 'A'
class A;
      ^

¿Qué compilador es correcto según el estándar de C ++?

¿Hay algún interruptor provisto por clang para compilar tal código? Sashank
Ya que intentas crear un objeto de tipoA el compilador tiene que saber su tamaño, que no puede saber sin haber visto la definición completa, por lo que clang estaría aquí (pero no tengo la referencia estándar). Benjamin Bannier

Tu respuesta

4   la respuesta
0

Al compilador de Comeau tampoco le gusta:

"ComeauTest.c", line 5: error: incomplete type is not allowed
     A a;
       ^

Sin embargo, mis intentos de encontrar capítulo y verso en el estándar de C ++ fueron infructuosos. Parece oculto entre las líneas y las interacciones de "punto de instanciación", "resolución de nombres". Los párrafos 14.6 / 8 y 14.6 / 9 de la norma de 2003 parecen relevantes.

No tengo la especificación a mano, pero sé que se agregó una regla (como una resolución de DR) para hacer que su código esté "mal formado; no se requiere diagnóstico" en algún momento después de c ++ 98 o c ++ 03. se encuentra en el párrafo que habla de definiciones de plantillas mal formadas. Johannes Schaub - litb
1

clang++ está utilizando el comportamiento correcto, esto se describe en la sección4.6/9 de la norma (n1905).

Templates 14.6/9 Name resolution

Si un nombre no depende de un parámetro de plantilla (como se define en14.6.2), una declaración (o conjunto de declaraciones) para ese nombre deberá estar dentro del alcance en el punto donde el nombre aparece en la definición de la plantilla; el nombre está vinculado a la declaración (o declaraciones) que se encuentran en ese punto y este enlace no se ve afectado por declaraciones que son visibles en el momento de la instanciación.

Para poner las cosas en términos más simples; si el nombre esno dependiendo de un parámetro de plantilla, debe estar en el alcance donde se encuentra la definición; por lo tanto tendrás que definirA antes de su definición detemplate<typename T> void fun ().

N1905 es un poco viejo. Es a partir de 2005. N3291 fue (creo) el borrador final. David Hammen
5

Clang es correcto, que yo sepa. En su función divertida, no sabe el tamaño de A, y como asigna una A, necesita saber su tamaño. En mi opinión, gcc es la manera de perdonar aquí.

clang está siendo agradable al detectar el problema, pero la falta de diagnóstico de gcc también es correcta. Ver mi respuesta para más detalles. David Hammen
12

Ambos son correctos. Este es un programa mal formado. Énfasis mío:

N3290 14.6¶9
Si un tipo utilizado en un nombre no dependiente está incompleto en el punto en el que se define una plantilla, pero está completo en el punto en el que se realiza una instanciación, y si la integridad de ese tipo afecta si el programa está bien o no. Formado o afecta la semántica del programa,el programa está mal formado; no se requiere diagnóstico.

El hecho de que clang ++ y otros compiladores emitan un diagnóstico aquí es una característica agregada agradable a tener, pero un diagnóstico no es obligatorio. Esa cláusula "el programa está mal formado; no se requiere ningún diagnóstico" le da al desarrollador del compilador la libertad de hacer casi cualquier cosa en tales circunstancias y seguir cumpliendo con los requisitos.

La intención es permitir el procesamiento perezoso de la plantilla. El estándar permite que las implementaciones aplacen el análisis detallado hasta el punto en el que se crea una instancia de una plantilla. ClaseA Se conoce en el punto de instanciación, por lo que se compila. Todavía está mal formado porque otro archivo podría#include La definición de tu plantilla, define una clase completamente diferente.A, y crear un diferentefun<int>(). Esto no se podría vincular porque viola la regla de una definición (incluso si calificasfun comoinline). David Hammen
¿Por qué entonces el programador ni siquiera es informado por g ++ de que algo está mal? ¿debería ser para que ambos compiladores estén en lo correcto pero uno haga su trabajo y el segundo haga algo muy diferente? scdmb

Preguntas relacionadas