Отличающийся тип возврата для виртуальных функций - PullRequest
10 голосов
/ 28 января 2011

Тип возвращаемого значения виртуальной функции должен быть того же типа, что и в базовом классе, или ковариантным.Но почему у нас есть это ограничение?

Ответы [ 4 ]

13 голосов
/ 28 января 2011

Из-за чепухи, которая может возникнуть:

struct foo
{
    virtual int get() const { return 0; }
};

struct bar : foo
{
    std::string get() const { return "this certainly isn't an int"; }
};

int main()
{
    bar b;
    foo* f = &b;

    int result = f->get(); // int, right? ...right?
}

Не имеет смысла возвращать производному классу что-то совершенно не связанное.

5 голосов
/ 28 января 2011

Потому что как код, использующий возвращаемое значение, справится с возвращением всех видов несвязанных типов?например:

class A
{
public:
    virtual float func();
};

class B: public A
{
public:
    virtual char *func();
};

A *p = (some_condition) ? new A() : new B();
p->func();  // Oh no! What is the type?
3 голосов
/ 28 января 2011

Согласно стандарту C ++:

Тип возвращаемого значения переопределяющей функции должен быть либо идентичным типу возврата переопределенной функции, либо ковариантно классам функций.Если функция D :: f переопределяет функцию B :: f, возвращаемые типы функций ковариантны, если они удовлетворяют следующим критериям:

1) оба являются указателями на классы или ссылками на классы

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

3) оба указателя или ссылки имеют одинаковую квалификацию cv и тип класса в возвращаемом типе D:: f имеет ту же квалификацию cv, что и квалификация cv или меньше, чем тип класса в типе возврата B :: f.

1 голос
/ 07 августа 2012

Ответ очень похож на тот, который дан для «Почему я не могу назначить вектор для вектора ?» в FAQ Бьярна Страуструпа.

Возможность изменить тип возвращаемого значения может привести к пробелу в безопасности типов языка (см. Ответ от @GManNickG для конкретного примера) при работе с полиморфными типами.

Существует одна довольно распространенная ситуация, когда воздействие на возвращаемый тип будет идеальным: при возврате полиморфного указателя из виртуального метода базового типа. Например,

class Base {
public:
    virtual Base* parent() = 0;
};

class Child : public Base {
public:
    Base* parent() override
    {
        return parent_;
    }
private:
    Parent* parent_;  // Assume `Parent` type exists.
};

Здесь мы потеряли информацию о типе, которую Child знает о своем parent_ члене. Это приводит к большому количеству приведений, хотя тип в определенный момент был четко определен. Мы можем решить эту проблему с помощью идиомы (CRTP),

template<class ParentType>
class Base {
public:
    virtual ParentType* parent()
    {
        return parent_;
    }

private:
    ParentType* parent_;

};

class Child : public Base<Parent> {
};
...