Usted ha encontrado una de las esquinas extrañas de C++. En este caso, C++ no considera que dos funciones virtuales heredadas de diferentes clases sean la misma función aunque tengan el mismo nombre y firma de tipo.
Existen algunas buenas razones para que C++ actúe de esta manera. Por ejemplo, con frecuencia ocurre que esas dos funciones realmente no son lo mismo, a pesar de que tienen el mismo nombre y firma de tipo. El significado semántico de las dos funciones es diferente.
Aquí se muestra un ejemplo:
namespace vendor1 {
class Circle {
public:
virtual ::std::size_t size() const { return sizeof(*this); }
};
} // namespace vendor1
namespace vendor2 {
class Shape {
public:
virtual double size() const = 0;
};
class Circle : public Shape {
public:
virtual double size() const { return radius_ * radius_ * M_PI; }
};
} // namespace vendor2
a continuación, intenta esto:
namespace my_namespace {
class Circle : public ::vendor1::Circle, public ::vendor2::Circle {
// Oops, there is no good definition for size
};
Así que hay que recurrir a esto:
namespace my_namespace {
class Vendor1Circle : public ::vendor1::Circle {
public:
virtual ::std::size_t data_structure_size() const { return size(); }
};
class Vendor2Circle : public ::vendor2::Circle {
public:
virtual double area() const { return size(); }
};
class Circle : public Vendor1Circle, public Vendor2Circle {
// Now size is still ambiguous and should stay that way
// And in my opinion the compiler should issue a warning if you try
// to redefine it
};
Así, C++ tiene una buena razón tratar las funciones virtuales con la misma firma de tipo (el tipo de devolución no es parte de la firma de tipo) y el nombre de dos diferentes nt bases como funciones diferentes.
En cuanto a using
va ... Toda una directiva using
dice es "Agregue los nombres de este otro espacio de nombre a este espacio de nombres como si hubiera sido declarado aquí". Este es un concepto nulo en lo que respecta a las funciones virtuales. Simplemente sugiere que cualquier ambigüedad al usar un nombre debería resolverse de una manera diferente. Solo declara un nombre, no define el nombre. Para anular una función virtual, se requiere una nueva definición.
otoh, si se pone en un simple redefinición golpe seco en línea como esta:
class Bar : public Foo, public Goo {
public:
virtual DerivedElem& get_elem() { return Goo::get_elem(); }
};
un buen compilador debe ver eso y saber que ni siquiera se molestan en crear la función, y en lugar de simplemente tocar el violín las entradas de la tabla virtuales para hacer lo correcto. Puede que necesite emitir código para ello y tener el símbolo disponible en caso de que se tome su dirección, pero aún así debería ser capaz de manipular la tabla virtual para que la función desaparezca por completo cuando se llame a través de Foo *
.
Ejemplo ordenado, pequeño, autónomo. Deseo que todos los que tenían una pregunta sobre el código hicieran lo que hicieron. – Omnifarious
¿No debería derivarse Goo de Foo para tipos de retorno covariantes? – Chubsdad
Hola chicos, gracias por las respuestas rápidas y útiles. Buen consejo sobre la asignación polimórfica Steve. En caso de que alguien se preguntara por qué estoy mirando una estructura así, en mi código real 'Goo' es una clase de plantilla heredada por clases de plantilla subsiguientes y 'Foo' es una 'interfaz' para acceder a las clases de rango. – Tom