virtuales ventajas protegidos programacion herencia funciones constructores concepto clase atributos abstracta c++ templates

ventajas - C++: ¿Se puede detectar la herencia virtual en el momento de la compilación?



herencia programacion (8)

¿Has probado SUPERSUBCLASS de Loki?

http://loki-lib.sourceforge.net/

Me gustaría determinar en tiempo de compilación si un puntero a Derivado se puede convertir desde un puntero a Base sin dynamic_cast <>. ¿Es esto posible usar plantillas y metaprogramación? Este no es exactamente el mismo problema que determinar si Base es una clase base virtual de Derived, porque Base podría ser la superclase de una clase base virtual de Derived.

Gracias, Tim Update: Me sentí bien con este método:

#include <iostream> using namespace std; class Foo { }; class Bar : public Foo { }; class Baz : public virtual Foo { }; class Autre : public virtual Bar { }; typedef char Small; class Big { char dummy[2]; }; template<typename B, typename D> struct is_static_castable { const B* foo; char bar[1]; static Small test(char(*)[sizeof(static_cast<const D*>(foo)) == sizeof(const D*)]); static Big test(...); enum { value = (sizeof(test(&bar)) == sizeof(Small)) }; }; int main() { cout << "Foo -> Bar: " << is_static_castable<Foo, Bar>::value << "/n"; cout << "Foo -> Baz: " << is_static_castable<Foo, Baz>::value << "/n"; cout << "Foo -> Autre: " << is_static_castable<Foo, Autre>::value << "/n"; }

Pero no funciona con gcc:

multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Baz>’: multi-fun.cpp:38: instantiated from here multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Baz’ via virtual base ‘Foo’ multi-fun.cpp:29: error: array bound is not an integer constant multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Autre>’: multi-fun.cpp:39: instantiated from here multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Autre’ via virtual base ‘Bar’ multi-fun.cpp:29: error: array bound is not an integer constant

¿Estoy confundido acerca de lo que se puede hacer con el truco sizeof ()?


Aquí hay una solución para redireccionar el compilador para que haga algo dependiendo de si la clase es una subclase de otra o no.

class A {}; class B : virtual public A {}; class C : public A {}; // Default template which will resolve for // all classes template < typename T , typename Enable = void > struct FooTraits { static void foo(){ std::cout << "normal" << std::endl; } }; // Specialized template which will resolve // for all sub classes of A template < typename T > struct FooTraits < T , typename boost::enable_if < boost::is_virtual_base_of< A, T> >::type > { static void foo(){ std::cout << "virtual base of A" << std::endl; } }; int main(int argc, const char * argv[] ){ FooTraits<C>::foo(); // prints "normal" FooTraits<B>::foo(); // prints "virtual base of A" }

Y si quieres saber cómo lo impulsó lo hizo. Si tiene la clase Base y la clase Derivada, se mantiene lo siguiente.

struct X : Derived, virtual Base { X(); X(const X&); X& operator=(const X&); ~X()throw(); }; struct Y : Derived { Y(); Y(const Y&); Y& operator=(const Y&); ~Y()throw(); }; bool is_virtual_base_of = (sizeof(X)==sizeof(Y)));

Es un truco de usar la herencia virtual con herencia múltiple. La herencia múltiple de la misma base virtual no produce duplicados de la clase base virtual y, por lo tanto, puede probar esto con sizeof.


Esto puede ser un poco ingenuo (soy mucho más fuerte en C que en C ++), por lo que podría no entender lo que estás tratando de hacer, pero si estás hablando de punteros de fundición, los moldes de estilo C funcionan perfectamente bien (por ejemplo, (D *)foo ), o el reinterpret_cast equivalente de C ++. Dicho esto, esto puede ser muy peligroso, porque no tiene ninguna verificación de tiempo de ejecución, y por lo tanto necesita estar seguro de que está convirtiendo en el tipo correcto. Por otra parte, si desea tener una forma fácil de verificar si se trata de una suposición correcta o no, volvemos al punto de partida. Sin embargo, parece que está intentando comparar los punteros de arriba, que son todos iguales (básicamente son enteros). Que yo sepa, no hay manera de determinar la clase de un objeto en tiempo de ejecución en C ++, incluyendo sizeof, que funciona en tiempo de compilación. Básicamente, no hay forma de hacer lo que quieres hacer (al menos no con C ++ estándar), sin embargo, el lanzamiento real no causará ningún problema, solo usar el puntero recién lanzado de forma incorrecta. Si necesita absolutamente esta funcionalidad, probablemente sería mejor que incluya una función virtual en su clase base que informe de qué clase es (preferiblemente con un valor enumeración), y la sobrecargue en cualquier subclase que desee determinar si puede lanzar también .


Hay una plantilla de hack para hacerlo en tiempo de compilación.

Primero necesitas crear una clase de interfaz como esta:

template <typename T> class SomeInterface { public: inline int getSomething() const; }; template<typename T> inline int SomeInterface<T>::getSomething() const { return static_cast<T const*>(this)->T::getSomething(); }

La idea es: convertir this en T y llamar al método con el mismo nombre y los mismos argumentos. Como puede ver, la función de envoltura está en línea, por lo que no habrá sobrecarga de rendimiento o de pila de llamadas durante el tiempo de ejecución.

Y luego crear clases implementando la interfaz como esta:

class SomeClass : public SomeInterface<SomeClass> { friend class SomeInterface<SomeClass>; public: int getSomething() const; };

Luego simplemente agregue implementaciones de los métodos derivados normalmente.

De esta manera, puede que no se vea hermoso, pero hace exactamente el trabajo.


Primero, su código está haciendo el tamaño de un puntero en lugar de un puntero desreferenciado, por lo que no funcionaría aunque gcc no se quejara.

En segundo lugar, el tamaño del truco tiene que funcionar con lanzamientos de 0 y no con punteros u objetos reales, lo que garantiza una sobrecarga de cero y también que no cumplirá hasta que lo haga correctamente.

En tercer lugar, debe declarar 2 clases o estructuras con plantillas, una derivada solo de D, la otra derivada de D y B virtual, y luego emitir 0 a sus punteros, eliminarlas de ellas y luego sizeof.

4º - ¿Tiene alguna razón importante para intentar ser políticamente correcto con static_cast en lugar de un lanzamiento directo aquí? El compilador siempre deducirá de lo que está buscando quejarse más y, en este caso, definitivamente no lo está.

Por cierto, no es necesario que obtenga el código completo de Alexandrescu; solo tome la técnica básica que básicamente:

sizeof(*((T*)0))

Alexandrescu es realmente bueno limpiando después de un truco.

Ah, y recuerde que no se supone que el compilador evalúe el tamaño de argumentos o que cree instancias de clases y estructuras sin usar, así que si lo hace, entonces es un error del compilador y si lo obliga a hacerlo, entonces es su error :-)

Una vez que tenga eso, debe definir con precisión y en términos positivos su declaración "si un puntero a Derivado se puede convertir de un puntero a Base sin dynamic_cast <>" en realidad significa en términos de relaciones de clase, simplemente diciendo "sin operador / la función Q "no hace que un problema esté bien definido y no puede resolver lo que no puede definir - honestamente :-)

Así que simplemente tome el primer paso limpio que se compile y luego intente definir de qué manera los dos casos que mencionó serían diferentes en la realidad: lo que uno tendría o haría que el otro no.


Si desea saber en tiempo de compilación, puede tomar la clase derivada como un parámetro pero, si lo único que tiene es la Base, entonces no puede saber si se refiere a cualquiera de las clases foo, bar, etc. Esta comprobación solo se puede realizar cuando el puntero se convierte en una Base. Creo que ese es todo el propósito de dynamic_cast <>


Tuve el mismo problema una vez. Desafortunadamente, no estoy muy seguro del problema virtual. Pero: Boost tiene una clase llamada is_base_of (ver here ) que le permitiría hacer algo. como el siguiente

BOOST_STATIC_ASSERT((boost::is_base_of<Foo, Bar>::value));

Además, hay una clase is_virtual_base_of en is_virtual_base_of de Boost, tal vez eso es lo que estás buscando.


Una vez convertido al puntero base, solo puede obtener un error de tiempo de ejecución (dynamic_cast). Puede definir los métodos utilizando parámetros con plantillas y obtener un error de compilación mediante el uso de especializaciones de plantillas.