¿Usar typeid en un comportamiento indefinido de tipo declarado hacia adelante?

¿Estoy leyendo el estándar correctamente en 5.2.8.3: ... If the type of the expression is a class type, the class shall be completely-defined. Si el tipo no está "completamente definido", ¿significa que el siguiente programa no está definido?

foo.cpp:

struct foo
{
  virtual void a(){}
};

struct bar : foo
{
  virtual void a(){}
};

bar abar;

foo& get_some_foo()
{
  return abar;
}

principal.cpp:

#include <iostream>
#include <typeinfo>

struct foo;

foo& get_some_foo();

int main()
{
  foo& a_ref_foo(get_some_foo());

  std::cout << "a_ref_foo typeid name: " << typeid(a_ref_foo).name() << std::endl;

  return 0;
}

Salidas de MSVC10: `a_ref_foo typeid name: struct foo'

preguntado el 04 de julio de 12 a las 01:07

3 Respuestas

Cuando compilo tu código con:

g++ foo.cpp main.cpp -o main

Yo obtengo:

main.cpp: In function ‘int main()’:
main.cpp:12:52: error: invalid use of incomplete type ‘struct foo’
main.cpp:4:8: error: forward declaration of ‘struct foo’

Eso está de acuerdo con mi interpretación de la norma, que no se puede aplicar typeid a un tipo incompleto, y a_ref_foo es de un tipo incompleto, ya que la definición completa del tipo foo no es visible. main.cpp (con las líneas que agregué) está mal formado y se requiere un diagnóstico.

Actualización:

He reproducido el problema con Visual Studio 2010 Express. Incluso con las extensiones de idioma deshabilitadas, este programa trivial:

#include <typeinfo>

struct foo;

int main()
{
  typeid (foo);
  return 0;
}

compilado sin mensajes de diagnóstico. Con gcc 4.7, obtengo:

main.cpp: In function ‘int main()’:
main.cpp:7:14: error: invalid use of incomplete type ‘struct foo’
main.cpp:3:8: error: forward declaration of ‘struct foo’

La misma regla:

Si el tipo de la expresión es un tipo de clase, la clase debe estar completamente definida.

aparece en las versiones de 1998, 2003 y 2012 del estándar ISO C++.

Parece un error en Visual Studio. (Si alguien desea informar esto a Microsoft, adelante).

Respondido 04 Jul 12, 06:07

En este caso, el uso de "deberá" significa que está mal formado, porque es una regla diagnosticable (es decir, no se dice que "no se requiere diagnóstico" y no se describe explícitamente como un comportamiento indefinido). - R. Martinho Fernández

@R.MartinhoFernandes: Gracias, actualicé la respuesta. (El estándar C, con el que estoy más familiarizado, usa "deberá" de manera diferente; una violación de un "deberá" que no está en una forma explícita restricción hacer que el comportamiento del programa sea indefinido. Las reglas de C++ se tratan en la sección 1.4, "Cumplimiento de la implementación".) - Keith Thompson

Su suposición acerca de las inclusiones era correcta. Lo siento por eso. También probé en gcc y obtuve los mismos errores. Su interesante MSVC 10 con w4 ni siquiera avisa. - Zac

@Zac: Sí, y lo reproduje yo mismo con un caso de prueba mucho más simple, incluso con las extensiones de idioma deshabilitadas. La misma regla aparece en las versiones de 1998, 2003 y 2011 del estándar C++. - Keith Thompson

Sí, el programa está mal formado (en lugar de causar un comportamiento indefinido).

Si te preguntas el porqué, entonces debes considerar que typeid es un solo operador, pero tiene una semántica completamente diferente para tipos polimórficos que para tipos no polimórficos. En particular, si foo es polimórfico (tiene al menos una función virtual, entonces typeid producirá una referencia a la type_info objeto para el tipo real (en este caso bar) mientras que si el tipo no tiene ninguna función virtual, devolverá una referencia al type_info objeto para el estático tipo de expresión (en este caso foo).

Para que el compilador genere el código apropiado, el compilador debe sabes qué en el lugar donde typeid se utiliza cuál de los dos casos se aplica. Si el tipo está incompleto, esa información no está presente para el compilador.

Respondido 04 Jul 12, 03:07

Tu por qué tiene sentido: eso es lo que estaba asumiendo, el compilador solo ve un tipo no polimórfico. Me encontré con este comportamiento y estaba investigando el estándar tratando de averiguar qué significaba. Gracias. - Zac

@Zac: el compilador debería haber rechazado su código. No es un comportamiento indefinido, sino un programa mal formado que el compilador puede diagnosticar de manera trivial. - David Rodríguez - Dribeas

Esa distinción (tipo dinámico frente a tipo estático) no se aplica a los usos de typeid donde el argumento es un nombre de tipo, que GCC al menos también marca como un error, por ejemplo: struct Foo; const std::type_info &fooType = typeid(Foo); - Arne Vogel

¡Esta debería haber sido la respuesta! - Nawaz

Su programa es completamente correcto porque funciona get_some_foo y bar son globales. Entonces bar está completamente definido, pero si define una variante de bar in main.cpp, luego utilícelo como un parámetro en typeid , o se producirá un error al compilar.

Respondido 31 Jul 12, 22:07

En la unidad de traducción donde main se define, el tipo foo is incompleto. - David Rodríguez - Dribeas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.