virtuales que puro puras polimorfismo objeto modificador herencia funciones c++ templates inheritance compiler-errors pragma

que - polimorfismo puro c++



¿Cómo puedo forzar un error de compilación si se llama a un método virtual no reemplazado? (2)

Esta es una pregunta bastante general sobre el estilo y la seguridad al escribir una clase base de plantilla en C ++. Tenga paciencia conmigo, sin embargo, hay una pregunta específica al final ...

Tengo una clase base de plantilla que implementa completamente la funcionalidad requerida cuando el tipo T es de tipo primitivo (int, float, etc.). Sin embargo, si T no es primitivo (por ejemplo, si T es una clase que necesita tener un constructor llamado con un conjunto específico de argumentos) entonces algunos de los métodos deberán anularse, y por esta razón se declaran virtuales en la plantilla. La plantilla también contiene un método virtual puro que lo fuerza a ser abstracto, por lo que para usarlo debe derivarse de: una clase derivada basada en un tipo primitivo puede usar todos los métodos como se proporcionan y anular solo el virtual puro, mientras que una clase derivada basada en un tipo no primitivo debe anular todos los métodos virtuales.

Por ejemplo:

template <typename T> class myTemplate { public: // Constructor: myTemplate<T>(); // Destructor: virtual ~myTemplate(); // General function, valid for all classes derived from this: void printMe() { printf("Hello, I''m from the template/r/n"); } // Primitive function, must be overridden for non-primitive types: virtual T DoSomething(const T& myClass) { T result = result + static_cast<T>(1); return result; } // Primitive function, must be overridden for non-primitive types: virtual T DoSomethingElse(const T& myClass) { T result = result - static_cast<T>(1); return result; } // Pure Virtual function, must be overridden in all cases: virtual void RefrainFromDoingAnythingAtAll(const T& myClass) = 0 }; class myIntegerClass : public myTemplate<int> { public: virtual void RefrainFromDoingAnythingAtAll(const int& myInteger) {} };

Supongamos ahora que quiero crear una clase derivada donde espero que se llame ''DoSomething'', pero no puedo imaginar ninguna circunstancia bajo la cual ''DoSomethingElse'' sea útil. Por lo tanto, me gustaría volver a implementar ''DoSomething'', pero no molestarme con ''DoSomethingElse''. Sin embargo, si en algún momento se llama al método ''DoSomethingElse'' de la clase derivada (ya sea porque realmente quise llamar ''DoSomething'' pero escribí el error por error, o porque surgió una circunstancia que antes no había previsto), entonces Quiero que se emita una advertencia del compilador para recordarme que no puedo hacer esto a menos que vuelva a implementar ''DoSomethingElse'' primero.

Por ejemplo:

class myWatermelonClass : public myTemplate<Watermelon> { public: virtual Watermelon DoSomething(const Watermelon &myWatermelon) { // Just return a copy of the original: Watermelon anotherWatermelon(myWatermelon); return anotherWatermelon; } virtual Watermelon DoSomethingElse(const Watermelon &myWatermelon) { // This routine shouldn''t ever get called: if it does, then // either I''ve made an error or, if I find that I really need // to reimplement it after all, then I''d better buckle down // and do it. < How do I make the compiler remind me of this? > } virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {} };

Obviamente, sé sobre las directivas de compilador estándar #error y #warning, pero si utilizo una de estas, entonces el error (o advertencia) se marca cada vez que compilo. Lo que quiero es asegurarme de que se produce un error en el momento de la compilación si llamo negligentemente

DoSomethingElse(aSpecificWatermelon);

desde algún lugar de mi código, pero no de otra manera . ¿Hay una manera de lograr esto? ¿O es solo un diseño fundamentalmente malo en primer lugar?


Al usar static_assert , puede causar un error de compilación cuando intenta llamar a una función que no cumple con ciertos criterios. No hay necesidad de funciones virtuales en absoluto. Aquí hay un ejemplo:

#include <iostream> #include <type_traits> using std::cout; template <typename T> struct myTemplate { void printMe() { cout << "Hello, I''m from the template/r/n"; } T DoSomething(const T& myClass) { static_assert( std::is_fundamental<T>::value, "DoSomething must be redefined in derived classes " "for non-fundamental types." ); T result = myClass + static_cast<T>(1); return result; } T DoSomethingElse(const T& myClass) { static_assert( std::is_fundamental<T>::value, "DoSomethingElse must be redefined in derived classes " "for non-fundamental types." ); T result = myClass - static_cast<T>(1); return result; } template <typename U> struct never_true { static const bool value = false; }; void RefrainFromDoingAnythingAtAll(const T&) { static_assert( never_true<T>::value, "RefrainFromDoingAnythingAtAll must be redefined " "in derived classes." ); } }; struct Watermelon { }; struct Lemon { }; struct myIntegerClass : myTemplate<int> { void RefrainFromDoingAnythingAtAll(const int &) { } }; struct myWatermelonClass : myTemplate<Watermelon> { Watermelon DoSomething(const Watermelon&) { return Watermelon(); } Watermelon DoSomethingElse(const Watermelon&) { return Watermelon(); } void RefrainFromDoingAnythingAtAll(const Watermelon &) { } }; struct myLemonClass : myTemplate<Lemon> { }; int main() { myIntegerClass x; x.DoSomething(5); // works x.DoSomethingElse(5); // works x.RefrainFromDoingAnythingAtAll(5); // works myWatermelonClass y; y.DoSomething(Watermelon()); // works y.DoSomethingElse(Watermelon()); // works y.RefrainFromDoingAnythingAtAll(Watermelon()); // works myLemonClass z; z.DoSomething(Lemon()); // error z.DoSomethingElse(Lemon()); // error z.RefrainFromDoingAnythingAtAll(Lemon()); // error }


Creo que está haciendo un mal uso de las plantillas y la mezcla de despacho virtual. Por ejemplo, tu clase base no va a compilar para ningún tipo que no sea compatible con el operador de suma y sea construible desde un int. Cada vez que myTemplate se especializa implícitamente, todas las funciones virtuales se compilarán, incluso si las anulas en clases derivadas:

virtual T DoSomething(const T& myClass) { T result = result + static_cast<T>(1); // compile error when T == Watermelon return result; }

En tu caso, lo que estás buscando es la especialización explícita de plantillas:

template <> class myTemplate<Watermelon> { public: // General function, valid for all classes derived from this: void printMe() { printf("Hello, I''m from the Watermelon template specialisation/r/n"); } virtual Watermelon DoSomething(const Watermelon &myWatermelon) { // Just return a copy of the original: Watermelon anotherWatermelon(myWatermelon); return anotherWatermelon; } // notice there''s no DoSomethingElse! virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {} };

Ahora llamando a DoSomethingElse en una instancia de myTemplate<Watermelon> inmediatamente le dará un error de compilación, ya que no hay tal función.