переопределяемый тип возвращаемой виртуальной функции отличается и не является ковариантным - PullRequest
7 голосов
/ 07 августа 2011

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

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

 '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, если это имеет значение.

<ч />

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

Ответы [ 6 ]

17 голосов
/ 07 августа 2011

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

2 голосов
/ 28 мая 2013

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

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

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

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

2 голосов
/ 07 августа 2011

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

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

0 голосов
/ 29 июля 2013

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

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

}

inr должен быть как статического, так и динамического типа "Inner" (не "Inner2"), чтобы быть размещенным вне стека - так что, если бла вернет Inner2, он будет использован для создания Inner, а его "2ness" будет потерян (или нарезан, как упоминает К Дракон).

0 голосов
/ 27 мая 2013

Почему это?

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

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

0 голосов
/ 07 августа 2011

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

...