s->duplicate()
возвращает объект типа Box*
, но я получаю сообщение об ошибке при инициализации с Box*
, Похоже, он превращается обратно в Shape*
, Какой смысл иметь ковариантные возвращаемые типы, если он конвертируется обратно в указатель базового класса ?:
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();
}
Ошибка:
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.
Хотя Box::duplicate
является вызывается во время выполнения (через виртуальную диспетчеризацию), и хотя Box::duplicate
делает переопределение Shape::duplicate
(ковариантно), и хотя Box::duplicate
делает вернуть Box*
, вы все равно получите Shape*
указатель, потому что вы звоните duplicate()
через Shape*
указатель и Shape*
тип возвращаемого значения Shape::duplicate()
и компилятор видит только то, что вы звоните Shape::duplicate
не Box::duplicate
,
C ++ не может динамически выбирать типы, поэтому это лучшее, что он может сделать. Ваш Box*
автоматически преобразуется в Shape*
на выходе из Box::duplicate
, Как сказал Барри, «он все еще должен компилироваться во время компиляции, и во время компиляции все, что мы знаем, это то, что он возвращает Shape*
».
Затем, чтобы превратить его в Box*
опять же, вам нужно явно привести его (используя static_cast
или же dynamic_cast
) потому что неявного преобразования не существует.
[C++11: 10.3/7]:
Тип возврата переопределяющей функции должен быть либо совпадает с типом возврата переопределенной функции, либо ковариантный с классами функций. [..]
[C++11: 10.3/8]:
Если тип возвратаD::f
отличается от типа возвратаB::f
тип класса в возвращаемом типеD::f
должен
быть полным в момент объявленияD::f
или должен быть типом классаD
, Когда переопределяющая функция вызывается как последняя переопределенная переопределенная функция, ее результат преобразуется в тип, возвращаемый (статически выбранной) переопределенной функцией. (5.2.2). [..]
В стандартном тексте приведен соответствующий пример.
Дело не в том, чтобы сделать это:
Box* b = s->duplicate();
Это, очевидно, не может работать, так как Shape::duplicate()
возвращает Shape*
, Дело скорее в том, чтобы принять Box*
если ты звонишь duplicate()
на Box
непосредственно:
Box* old = new Box;
Box* b = old->duplicate(); // OK! We know it's a Box