online ejemplos descargar definicion caracteristicas c++

ejemplos - Colisión de nombre de clase C++



c++ online (2)

Para el siguiente código C ++, estoy obteniendo un comportamiento inesperado. El comportamiento fue verificado con GCC reciente, Clang y MSVC ++. Para activarlo, es necesario dividir el código entre varios archivos.

def.h

#pragma once template<typename T> struct Base { void call() {hook(data);} virtual void hook(T& arg)=0; T data; };

foo.h

#pragma once void foo();

foo.cc

#include "foo.h" #include <iostream> #include "def.h" struct X : Base<int> { virtual void hook(int& arg) {std::cout << "foo " << arg << std::endl;} }; void foo() { X x; x.data=1; x.call(); }

bar.h

#pragma once void bar();

bar.cc

#include "bar.h" #include <iostream> #include "def.h" struct X : Base<double> { virtual void hook(double& arg) {std::cout << "bar " << arg << std::endl;} }; void bar() { X x; x.data=1; x.call(); }

main.cc

#include "foo.h" #include "bar.h" int main() { foo(); bar(); return 0; }

Rendimiento esperado:

foo 1 bar 1

Salida real:

bar 4.94066e-324 bar 1

Lo que esperaba que sucediera:

Dentro de foo.cc, se crea una instancia de X definida dentro de foo.cc y, a través de call call (), se llama a la implementación de hook () dentro de foo.cc. Lo mismo para el bar.

Lo que realmente sucede:

Una instancia de X como se define en foo.cc se hace en foo (). Pero al llamar a la llamada, no se envía a hook () definido en foo.cc sino a hook () definido en bar.cc. Esto lleva a la corrupción, ya que el argumento para enganchar sigue siendo un int, no un doble.

El problema se puede resolver al colocar la definición de X en foo.cc en un espacio de nombres diferente al de la definición de X en bar.cc

Así que finalmente la pregunta: No hay una advertencia del compilador sobre esto. Ni gcc, ni clang o MSVC ++ mostraron una advertencia al respecto. ¿Es ese comportamiento válido según lo definido por el estándar de C ++?

La situación parece estar un poco construida, pero sucedió en un escenario del mundo real. Estaba escribiendo pruebas con quickcheck, donde las posibles acciones en una unidad a probar se definen como clases. La mayoría de las clases de contenedor tienen acciones similares, por lo que al escribir pruebas para una cola y un vector, las clases con nombres como "Borrar", "Push" o "Pop" pueden aparecer varias veces. Como estos solo se requieren localmente, los puse directamente en las fuentes donde se ejecutan las pruebas.


El programa está mal formado porque viola la en.wikipedia.org/wiki/One_Definition_Rule al tener dos definiciones diferentes para la clase X Así que no es un programa válido de C ++. Tenga en cuenta que la norma permite específicamente a los compiladores no diagnosticar esta violación. Así que los compiladores se conforman, pero el programa no es válido en C ++ y, como tal, tiene un comportamiento indefinido cuando se ejecuta (y por lo tanto, cualquier cosa puede suceder).


Tiene dos clases X idénticas, pero diferentes, en diferentes unidades de compilación, lo que hace que el programa no esté bien formado, ya que ahora hay dos símbolos con el mismo nombre. Dado que el problema solo se puede detectar durante la vinculación, los compiladores no pueden (y no se requieren) reportar esto.

La única forma de evitar este tipo de cosas es colocar cualquier código que no esté destinado a ser exportado (en particular, todo el código que no se haya declarado en un archivo de encabezado) en un espacio de nombres anónimo o sin nombre :

#include "foo.h" #include <iostream> #include "def.h" namespace { struct X : Base<int> { virtual void hook(int& arg) {std::cout << "foo " << arg << std::endl;} }; } void foo() { X x; x.data=1; x.call(); }

y equivalentemente para bar.cc De hecho, este es el propósito principal (¿único?) De los espacios de nombres sin nombre.

El simple cambio de nombre de sus clases (por ejemplo, fooX y barX ) puede funcionar para usted en la práctica, pero no es una solución estable, porque no hay garantía de que estos nombres de símbolos no sean utilizados por alguna biblioteca oscura de terceros cargada en el enlace. tiempo de ejecución (ahora o en algún momento en el futuro).