s->duplicate()
gibt ein Objekt vom Typ . zurück Box*
, aber ich erhalte einen Fehler beim Initialisieren mit Box*
. Es sieht so aus, als ob es zurück in umgewandelt wird Shape*
. Welchen Sinn haben kovariante Rückgabetypen, wenn sie zurück in den Basisklassenzeiger konvertiert werden?:
struct Shape
{
virtual Shape* duplicate()
{
return new Shape;
}
};
struct Box : Shape
{
virtual Box* duplicate()
{
return new Box;
}
};
int main()
{
Shape* s = new Box;
Box* b = s->duplicate();
}
Fehler:
main.cpp:22:12: error: cannot initialize a variable of type 'Box *' with an rvalue of type 'Shape *'
Box* b = s->duplicate();
^ ~~~~~~~~~~~~~~
1 error generated.
Obwohl Box::duplicate
ist zur Laufzeit aufgerufen werden (über virtuellen Dispatch), und obwohl Box::duplicate
tut überschreiben Shape::duplicate
(kovariant) und obwohl Box::duplicate
tut zurück a Box*
, du bekommst immer noch ein Shape*
Zeiger, weil du anrufst duplicate()
durch ein Shape*
Zeiger, und Shape*
ist der Rückgabetyp von Shape::duplicate()
, und der Compiler sieht nur Sie aufrufen Shape::duplicate
, nicht Box::duplicate
.
C++ ist nicht in der Lage, Typen dynamisch auszuwählen, daher ist dies das Beste, was es tun kann. Deine Box*
wird automatisch in a . umgewandelt Shape*
auf dem weg aus Box::duplicate
. Wie Barry sagte, “muss es immer noch zur Kompilierzeit kompiliert werden, und zur Kompilierzeit wissen wir nur, dass es a . zurückgibt Shape*
“.
Dann, um es zu einem zu machen Box*
Auch hier müssen Sie es explizit umwandeln (mit static_cast
oder dynamic_cast
), da keine implizite Abwärtskonvertierung existiert.
[C++11: 10.3/7]:
Der Rückgabetyp einer überschreibenden Funktion ist entweder identisch mit dem Rückgabetyp der überschriebenen Funktion oder kovariant mit den Klassen der Funktionen. [..]
[C++11: 10.3/8]:
Wenn der Rückgabetyp von D::f
unterscheidet sich von der Rückgabeart von B::f
, der Klassentyp im Rückgabetyp von D::f
muss zum Zeitpunkt der Erklärung vollständig sein D::f
oder soll der Klassentyp sein D
. Wenn die überschreibende Funktion als letzte Überschreibung der überschriebenen Funktion aufgerufen wird, wird ihr Ergebnis in den von der (statisch ausgewählten) überschriebenen Funktion zurückgegebenen Typ konvertiert (5.2.2). [..]
Im Standardtext folgt ein entsprechendes Beispiel.
Der Punkt ist nicht, dies zu tun:
Box* b = s->duplicate();
Das kann offensichtlich nicht funktionieren, da Shape::duplicate()
gibt a . zurück Shape*
. Der Punkt ist vielmehr, a . zu akzeptieren Box*
wenn du anrufst duplicate()
auf einen Box
direkt:
Box* old = new Box;
Box* b = old->duplicate(); // OK! We know it's a Box
.