Enum ковариация для возврата виртуальной функции

Почему C ++ не распознает enum MyEnum : int как ко-вариант к int?

Пример: http://ideone.com/Ns1O2d

#include <iostream>

enum FooType : int
{
Crazy = 0,
Cool
};

enum BarType : int
{
Hello = Cool + 1,
World
};

class Foo
{
public:
Foo(void)
{
}

~Foo(void)
{
}

virtual int getType(void)
{
return Crazy;
}
};

class Bar : public Foo
{
public:
Bar(void)
{
}

~Bar(void)
{
}

virtual BarType getType(void)
{
return Hello;
}
};

int main(int argc, char* argv[])
{
Bar f = Bar();
std::cout << f.getType() << std::endl;
return 0;
}

Ошибка компиляции:

prog.cpp:43:18: error: conflicting return type specified for 'virtual BarType Bar::getType()'
prog.cpp:26:14: error:   overriding 'virtual int Foo::getType()'

2

Решение

Перечислимые типы без границ (т.е. обычные enumsв отличие от enum class а также enum struct) обеспечить неявное продвижение до целого числа, т.е. вы можете сделать это:

enum FooType : int { Crazy, Cool };
int val = Crazy; // promotion to integer

Однако, это не работает наоборот:

FooType val = 0;  // illegal

Это следует из §7.2 / 5: Каждое перечисление определяет тип, который отличается от всех других типов, в сочетании с §7.2 / 9: Значение перечислителя или объекта типа перечисления с незаданной областью преобразуется в целое число путем интегрального преобразования.

Я полагаю, что причина этого правила довольно очевидна: могут быть (и, как правило, существуют) целочисленные значения, для которых не определен соответствующий перечислитель. В приведенном выше примере преобразование 0 а также 1 было бы теоретически возможно, но преобразование 2 или любое большее число не может быть преобразовано.

Однако если бы перечисление было ковариант к его основному типу (int в вашем примере), в том смысле, в каком вы его определили, возможно следующее:

class Foo
{
public:
virtual ~Foo(void) {}
virtual int getType(void)
{
return Crazy;
}
};

class Bar : public Foo
{
public:
virtual ~Bar(void) {}
virtual BarType getType(void)
{
return Foo::getType();
}
};

В производном классе Bar::getType() теперь определено, чтобы вернуть BarType, но делает это, вызывая унаследованную функцию Foo::getType(), что вполне законно.

Если бы это было возможно, как написано, Bar::getType() пришлось бы неявно преобразовать int что вытекает из Foo::getType() для int, И это не может быть, как объяснено выше.


Тем не менее, вы все равно можете достичь того, что ваш код, похоже, намерен объявив Bar::getType так же, как Foo:getTypeи вернуть BarType (который неявно повышен до int):

class Bar : public Foo
{
public:
virtual ~Bar(void) {}
virtual int getType(void)
{
return Hello;
}
};

Обратите внимание, что это работает, только если базовый тип int (что это потому, что вы исправили это int в объявлении перечисления), и если перечисление не ограничено (то есть не использует enum class или же enum struct).

3

Другие решения

С ++ не работает так. Идея ковариантных возвращаемых типов работает только для ссылок или указателей на объекты, которые являются подклассами исходного возвращаемого типа.

Когда ты пишешь enum class MyEnum : int, вы не указываете подкласс, вы указываете, что MyEnum будет реализован типом int.

Вам разрешено только делать следующее:

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

class Derived : public Base {
public:
Derived* foo();
};
2

набранный enumгде только недавно приняли в C ++. Есть много вещей, которые enumмы должны делать это в идеальном мире — мы должны иметь возможность перебирать определенные значения, мы должны иметь возможность превращать их в алгебры, нам нужно разрешать расширять их, как мы это делаем classES, и мы, вероятно, должны быть в состоянии сделать virtual функция, которая возвращает ковариант enum как ты просишь.

Но мы не можем.

Теперь мы можем сделать следующее:

class Base {
public:
virtual int getType() const { return 0; }
};

enum Bird : int { Chicken = 0, Duck = 1 };

class Derived: public Base {
public:
Bird getBirdType() const { return static_cast<Bird>(getType()); }
virtual int getType() const override { return Chicken; }
};

где старая подпись (getType) остается открытым, с новой функцией (getBirdType) дает нам исправленную версию.

Мир мог бы стать лучше, если бы мы могли написать Bird Derived::getType() const override, но если вы не согласны с Лейбницем, мы не живем в этом мире. Добавление функций в C ++ требует стандартного времени разработки и времени компилятора для его реализации. Таким образом, функции, которые добавляются в C ++, как правило, являются теми, которые были протестированы в компиляторах на рынке, и стоимость которых стоит выгоды и спроса.

Если вы действительно хотите эту функцию, я призываю вас принять участие в усилиях по стандартизации C ++!

2

Почему C ++ не распознает enum MyEnum : int как ко-вариант к int?

Поскольку это не так — тогда как объекты производных типов классов могут неявно рассматриваться как объекты их открытых базовых типов, перечисления со строгим типом, ну, в общем, сильно типизированных. Т.е. они могут не неявно трактоваться как объекты их базового типа, и, как говорится, не являются ковариантными кандидатами.

2