c++ templates gcc clang

c++ plantilla extraña optimización



templates gcc (2)

Escribí una clase de plantilla de singleton al igual que boost hace:

template <typename _T> class Singleton { public : static _T* Instance() { static _T obj; return &obj; } protected : Singleton() {} private : struct ObjectCreator { ObjectCreator() { Singleton<_T>::instance(); } }; static ObjectCreator object_creator; }; template <typename _T> typename Singleton<_T>::ObjectCreator Singleton<_T>::object_creator;

Y escribí la función principal para probarlo.

#include "Singleton.h" class A : public Singleton <A> { public: int a; }; int main() { A::Instance()->a = 2; }

Sé que ObjectCreator mal la Instancia en el constructor de ObjectCreator , lo extraño es que puedo compilarlo correctamente con gcc-4.4.7, luego usé Clang-6.0, me golpeó con el error tipográfico.

Supongo que gcc puede hacer una optimización, porque no hice nada con ObjectCreator , por lo que ignoró el código de error.

Tengo dos preguntas:

  1. ¿Qué debo hacer para que gcc me informe ese error ( sin cambiar mi código ), como agregar un indicador del compilador?
  2. Si alguien tiene una explicación más confiable para esto? Algún documento oficial haría.

Ps: Soy consciente de que boost agregaría una función do_nothing en ObjectCreate y la llamaría desde Singleton<_T>:: Instance() para evitar esta optimización.


  • ¿Qué debo hacer para que gcc informe de ese error (sin cambiar mi código), como agregar un indicador del compilador?

Podría agregar una template class Singleton<float>; creación de instancias explícita template class Singleton<float>; (Simplemente elegí float como tipo, pero podría elegir algo más apropiado) para forzar a GCC a verificar la sintaxis. Consulte https://gcc.godbolt.org/z/ii43qX para ver un ejemplo.

Si simplemente desea la comprobación, también puede colocar esta instancia explícita en una unidad de compilación separada agregando otro archivo cpp a su proyecto.

Sin embargo, hacer una instanciación explícita es más fuerte que la instanciación implícita, ya que todos los miembros y métodos serán instanciados. Este comportamiento puede ser anwanted (ver ejemplos en la biblioteca estándar).

  • Si alguien tiene una explicación más confiable para esto? Algún documento oficial haría.

Los miembros estáticos no se inicializan implícitamente hasta que se usa de manera que se requiere su definición (esto es muy diferente a la instanciación explícita).

@StoryTeller encontró el párrafo correcto en el estándar

14.7.1 Creación de instancias implícita [temp.inst]

La creación de instancias implícita de una especialización de plantilla de clase provoca la creación de instancias implícita de las declaraciones, pero no de las definiciones o argumentos predeterminados, de las funciones de miembro de clase, clases de miembro, miembros de datos estáticos y plantillas de miembro; y provoca la instanciación implícita de las definiciones de uniones anónimas miembros. A menos que un miembro de una plantilla de clase o una plantilla de miembro haya sido explícitamente instanciado o explícitamente especializado, la especialización del miembro se crea implícitamente cuando se hace referencia a la especialización en un contexto que requiere que exista la definición del miembro; en particular, la inicialización (y cualquier efecto secundario asociado) de un miembro de datos estáticos no se produce a menos que el miembro de datos estáticos se utilice de una manera que requiera la definición del miembro de datos estáticos.

EDITAR Debe aceptar la respuesta de @StoryTeller ya que explicó correctamente los dos aspectos de su pregunta primero.


Si leo el estándar correctamente, no creo que su código esté mal formado ( _T el uso de un identificador _T ). Clang hacer un esfuerzo adicional es fantástico, pero GCC no se equivoca al aceptarlo como es.

La razón es que su programa solo contiene una instanciación implícita de su plantilla. Según N1905 1 (énfasis mío):

14.7.1 Creación de instancias implícita [temp.inst]

1 ... La creación de instancias implícita de una especialización de plantilla de clase provoca la creación de instancias implícita de las declaraciones, pero no de las definiciones o argumentos predeterminados, de las funciones de miembro de clase , las clases de miembro , los miembros de datos estáticos y las plantillas de miembro; y provoca la instanciación implícita de las definiciones de uniones anónimas miembros. A menos que un miembro de una plantilla de clase o una plantilla de miembro haya sido explícitamente instanciado o explícitamente especializado, la especialización del miembro se crea implícitamente cuando se hace referencia a la especialización en un contexto que requiere que exista la definición del miembro; en particular, la inicialización (y cualquier efecto secundario asociado) de un miembro de datos estáticos no se produce a menos que el miembro de datos estáticos se utilice de una manera que requiera la definición del miembro de datos estáticos.

Nada usa object_creator de una manera que requiera que exista su definición. Como tal, solo se comprueba la declaración. Además, solo la declaración de la class ObjectCreator debe ser instanciada, no su definición (o la definición de su constructor). Esto es por la misma razón por la que se puede definir una variable externa de un tipo de clase declarado hacia adelante:

extern class C c;

El párrafo anterior (y el hecho de que nada use object_creator ) solo requiere que el nombre de tipo y el nombre de objeto sean instanciados, para producir un efecto similar al de la declaración externa anterior.

Como resultado, GCC nunca tiene que verificar que la instance sea ​​válida. Yo diría que dado el párrafo anterior, incluso si no tuviera un error tipográfico, es bastante posible que object_creator no haga lo que cree que hace. Si su código funcionó, es solo porque la función obj estática local se inicializó en el primer uso (haciendo que ObjectCreator redundante).

En cuanto a por qué agregar una creación de instancias explícita (como @P i sugirió ) inmediatamente causa un error. Podemos ver aquí:

14.7.2 Creación de instancias explícita [temp.explicit]

7 La creación de instancias explícita de una especialización de plantilla de clase también crea una instancia explícita de cada uno de sus miembros (sin incluir los miembros heredados de las clases base) cuya definición es visible en el punto de la instanciación y que no se ha especializado explícitamente en la unidad de traducción que contiene la creación de instancias explícita .

Cuando hacemos eso, forzamos recursivamente todo a ser instanciado, y como resultado, comprobado.

1 - Este es un borrador de 2005. Muy cerca de C ++ 03, y por lo tanto, considero apropiado dado su uso de GCC 4.4.7.