переопределение типа возврата виртуальной функции отличается и не является ковариантным

Ах, так вернулся как раз вовремя.

Я получаю странную ошибку:

 'B::blah': overriding virtual function return type differs and is not covariant from 'A::blah'

вот код, вызывающий проблему:

class A {
public:
    class Inner { };

    virtual Inner blah() = 0;
};

class B : public A {
public:
    class Inner2 : public Inner { };

    Inner2 blah() {
        return Inner2();
    }
};

Я посмотрел ошибку, и в соответствии с страница, которую я нашел на сайте Microsoft, один из способов типов может быть ковариантным, если:

класс в возвращаемом типе B:: f является тем же классом, что и класс в возвращаемом типе D:: f или, является однозначной прямой или косвенной базой класс класса в возвращаемом типе D:: f и доступен в D

разве это не так с Inner и Inner2? Я использую Microsoft Visual C++ 2010, если это имеет значение.


ок, спасибо, Джон, я узнал, что только указатели и ссылки могут быть ковариантными. Почему так? Производный может быть приведен к базе, поэтому почему виртуальные функции с возвращаемыми типами, производными от того же самого, просто не приводят возвращаемый тип к одному из базовых классов? В мой пример, похоже, имеет смысл иметь (A*(new B))->blah() возвратить Inner что действительно Inner2 Это было брошено.

6 ответов


ковариантными могут быть только указатели и ссылки.


Если то, что вы запрашиваете, будет разрешено, это будет означать, что вызов blah() через базовый класс должен выполнить преобразование из Inner2 во внутренний... приведение невозможно, поскольку вызывающий объект отвечает за управление возвращаемым объектом (поскольку он возвращается не указателем/ссылкой, а значением) и зарезервирует для него место в стеке. Поэтому он может обрабатывать только внутренний, а не Inner2 или любой класс предков.

таким образом, у вас есть экземпляр внутреннего, а не Inner2... Так что ты ничего не получишь. преимущество...


представьте, что ваш пример сработал.

рассмотрим следующий код:

A *a = some_function();
A::Inner inner = a->blah();

если динамический тип a Это B*, тогда a->blah() звонки a::B->blah(), и возвращает B::Inner. Это тогда молча отрезанный в экземпляр A::Inner. В общем, это (и любой тип нарезки) не то, что вы хотите. Я считаю это хорошим ограничением.


почему это?

просто потому, что комитет по стандарту ISO C++ постановил так!

фундаментальной причины нет. Это можно сделать по-другому, ценой крошечной дополнительной сложности компилятора.


объекты, объявленные в стеке среды выполнения, должны быть известны компилятору во время компиляции:

void f(const A & a) {
   Inner inr = a.blah( );

}

inr должен иметь как статический, так и динамический тип "внутренний" (не "Inner2"), который будет выделен из стека, поэтому, если бла вернул Inner2, он будет использоваться для создания внутреннего, и это "2ness" будет потеряно (или нарезано, как упоминает K Dragon).


на blah метод должен возвращать A:: Inner во всех случаях-если вы думаете о причине, это довольно просто. Экземпляр B легко может быть обращен к базовому классу A. Теперь, если кто-то вызывает blah на A (базовый класс, какой объект он должен вернуть? Inner2 или внутренний?