¿Los métodos/operadores base devuelven el tipo base cuando se les llama en derivado (C++)?

Dado:

class Base:
{
  public:
  ...
  Base operator+( const Base& other );
  Base& scale( float num );
}

class Derived : public Base:
{
  public:
  ...
  Derived( const Base& other );
  float super_math_wizardry();
}

//A user can do the following:

Base b1,b2,b3;
Derived d1,d2,d3;

b3 = b1 + b2;
d3 = d1 + d2;

b3 = b1.scale( 2.0f );
d3 = d1.scale( 3.0f ); //scale returns Base& type that is converted to Derived

float x,y;

x = (d1+d2).super_math_wizardry(); //compiler type error since d1+d2 returns Base type
y = (d1.scale(4.0f)).super_math_wizardry(); //similar compiler error

x = Derived(d1+d2).super_math_wizardry(); //works
y = Derived(d1.scale(4.0f)).super_math_wizardry(); //works

¿Hay alguna manera de hacer que las dos primeras declaraciones funcionen sin volver a implementar todos los métodos Base en Derivado (crear métodos Derivados que llamen a métodos Base y devuelvan tipos Derivado) y sin requerir que el usuario haga conversiones/constructores de copias de llamadas?

EDITAR: todos los objetos derivados están en el conjunto de objetos base (como se necesita para la herencia de clase), pero no todos los objetos base están en el conjunto de objetos derivados. Tienen los mismos miembros de datos, pero un objeto derivado tiene un valor constante asignado a uno de esos miembros de datos (el mismo valor constante para todos los objetos derivados).

Hay muchos métodos que son específicos de Base o Derivado, pero la mayoría de los operadores y los descriptores de acceso set/get tienen el mismo comportamiento definido en los objetos Base y Derivado. Lo que estoy tratando de hacer es recuperar un Derivado o Derivado& cuando llamo a un método Base en un objeto Derivado (ya que estas operaciones están matemáticamente definidas para hacerlo), mientras obtengo una Base o una Base& cuando llamo a un método Base en un objeto Base.

CONTEXT: Base es una clase Matrix y Derived es una clase Vector (columna). El constructor Derived (const Base& other) se creó para obtener explícitamente un vector de una matriz de una sola columna (nx1).

Entonces yo quiero:

x = (d1+d2).super_math_wizardry(); //works
y = (b1+b2).super_math_wizardry(); //fails (although possibly at run-time since a nx1 Matrix is a column vector)

preguntado el 12 de junio de 12 a las 19:06

Nitpick irrelevante: ¿estás seguro? scale debe devolver una referencia? -

@SteveJessop ¿por qué no? scale podría "devolver *esto" para evitar la copia. -

@Matthais: ah, tonto de mí, no consideré eso scale podría ser un mutador. Mi nuevo detalle irrelevante es que operator+ Debe ser un const función miembro! -

Supongo que sugiriendo template <typename T> float super_math_wizardry(const T &t) { return Derived(t).super_math_wizardry(); } no sería bienvenido, porque en realidad hay muchas de estas funciones derivadas, por lo que no desea una gran cantidad de repeticiones para cada una más de lo que desea una gran cantidad de repeticiones para cada función en Base? Pero el hecho de que Derived tiene un Base constructor es inusual. Me sugiere que Derived no agrega miembros de datos y, por lo tanto, podría definir super_math_wizardry como una función libre en lugar de una función miembro para empezar, y deshacerse de Derived. -

Hice algunas ediciones para que quede más claro lo que estoy buscando y para proporcionar algo de contexto. -

2 Respuestas

Dado su contexto, creo que el problema fundamental que tiene es informar al compilador que el conjunto de Derived los objetos están cerrados bajo operator+. Lo sé, lo sabes, pero no hay un atajo especial en el lenguaje C++ para expresarlo. Necesitas implementar Derived Derived::operator+(const Derived&) const.

Probablemente haría el Derived(const Base &other) constructor explicit. Presumiblemente arroja una excepción si las dimensiones de other son incorrectos, por lo que no es algo que los usuarios deban esperar que suceda implícitamente. Necesitan saber que es correcto, por lo que también podrían tener que decir que quieren que suceda.

Respondido el 12 de junio de 12 a las 21:06

Eso es un fastidio, ya que en mi aplicación me preocupa la cobertura del código, por lo que casi duplicará mis casos de prueba. Les agradezco a todos ustedes por toda su ayuda. - user1445306

@ user1445306: bueno, tal vez podrías engañar un poco a tu metodología de prueba. Cambio for case in cases {assert(case.left + case.right == case.result)} a for case in cases { assert(case.left + case.right == case.result); if (case.result.cols == 1) assert(Derived(case.left) + Derived(case.right) == Derived(case.result);}. Luego, su métrica de cobertura de línea le dirá si sus casos de prueba realmente incluyen matrices de una sola columna, lo que probablemente valga la pena saber incluso si hay fue una forma inteligente de hacer lo que quieres. Sí, está probando dos unidades de código en paralelo, no solo una. Demándame ;-) - steve jesop

Bueno, la respuesta corta es, no.

Las funciones tienen un tipo de retorno de Base. Lo que le está pidiendo al compilador que haga no es diferente de hacer

Derived d1;
Base* b = &d1;
b->super_math_wizardry(); // This is also wrong since we don't know that b can be
                          // a derived class

Simplemente no hay forma de hacer esto porque no hay forma de que el idioma sepa la diferencia entre lo anterior y

Base* b1 = new Base();
b1->super_math_wizardry(); // This is just plain wrong

Respondido el 12 de junio de 12 a las 21:06

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