Почему следующий код является ковариационной ошибкой? не T *
ковариантный с void *
…?
struct Base { virtual void *foo(); };
struct Derived : Base { int *foo(); };
GCC говорит:
invalid covariant return type for 'virtual int* Derived::foo()'
Тип возврата переопределяющей функции должен быть идентичным
тип возврата переопределенной функции или ковариант с
классы функций. Если функцияD::f
переопределяет функцию
B::f
возвращаемые типы функций ковариантны, если они
удовлетворяют следующим критериям:
- оба указатели на классы, оба являются lvalue ссылками на классы, или оба rvalue ссылки на классы [сноска опущена]
- […]
Со страниц 294-5 D&Е:
После некоторого рассмотрения альтернатив мы решили разрешить
переопределениеB*
поD*
и изB&
поD&
гдеB
является
доступная базаD
, К тому же,const
можно добавить или
вычитается везде, где это безопасно. Мы решили не ослаблять правила
позволяют технически осуществимые преобразования, такие какD
к доступным
базаB
,D
дляX
для которогоD
имеет преобразование,int*
в
void*
,double
вint
и т.д. Мы чувствовали, что выгода от
разрешение таких преобразований путем переопределения не будет перевешивать
стоимость внедрения и возможность сбить с толку пользователей.
Ковариация между void*
а также T*
не допускается, потому что:
1. отсутствие согласованности.
Нынешний способ ковариации тривиален для понимания и не создает путаницы.
Просто представьте void*
тип ковариации допускается. Для 1 стадии деривации это хорошо, но тогда это создаст путаницу. например.:
struct void_ { virtual void* foo (); };
struct int_ : void_ { virtual int* foo (); };
struct double_ : int_ { virtual double* foo (); }; // int* to double* or void* to double*
В 3-й иерархии struct double_
пользователь будет сбит с толку, что хотя double*
в int*
не возможно, почему код все еще компилируется? Только после проверки самого верхнего класса void_
известно, что это из-за double*
в void*
является «ковариантным». То же самое относится и к компилятору 🙂
2. Проблема со ссылками
В случае занятий, возвращая B&
/D*
/DD*
возможно. Но то же самое невозможно с void&
и поэтому int&
и т.п.
3. Смешивание ковариаций
Если void*
разрешено, то следующее также разрешено непреднамеренно.
struct void_ { virtual void* foo (); };
struct Base : void_ { virtual Base* foo (); };
struct Derived : Base { virtual int* foo (); }; // Look at `int*`
Что приводит к путанице.
Ковариантный тип возвращаемого значения — это когда производный класс предоставляет более конкретный / более узкий тип возвращаемого значения для переопределенной функции. Тип возвращаемого производного класса называется ковариантным.
int * не относится к типу void *, тогда как нечто подобное отображает ковариантный тип возвращаемого значения:
struct Base {
virtual void *foo();
virtual Base* func();
};
struct Derived : Base {
// int *foo();
Derived* func(); //COVARIANT RETURN TYPE
};