Ответ 1
Сильно типизированные перечисления:
С++ 11 вводит строго типизированный enum
s, используя enum class
:
#include <iostream>
enum class Color
{
Green = 0
};
enum class Fruit
{
Banana = 0
};
int main() {
Color c = Color::Green;
switch (c)
{
case Fruit::Banana:
std::cerr << "Banana" << std::endl;
break;
}
return 0;
}
Этот код завершится неудачно, как вы надеялись:
test.cc:18:17: error: не удалось преобразовать '(Fruit) 0' из 'Fruit' в 'Color'
Примечание. enum class
больше не приводит к Green
и Banana
в пространство имен, поэтому вы должны явно писать Color::
и Fruit::
сейчас, но вы также получаете типы безопасности.
Проблемы с предупреждением в С++ 03
Я не думаю, что предупреждение об этом в С++ 03 имеет смысл, это в основном просто станет шумом.
Люди часто используют enum
как константы времени компиляции, даже для таких вещей, как бит-поля. Чтобы предупреждение было значимым, вам придется ловить такие вещи, как enum { foo=0xf }; int c = foo;
, и многие кодовые базы разбросаны с помощью преобразований int
/enum
. (Позволяя этому победить точку любой более сильной проверки типа).
Хуже, хотя будет enum
использоваться практически в любом виде метапрограммного контекста, где анонимный enum
не только используется взаимозаменяемо с типами int
на регулярной основе:
template <int I>
struct is_odd {
enum { value = !(I % 2) };
};
template <int I>
struct foo {
static void bar() { /* I is true */ }
};
template <>
struct foo<0> {
static void bar() { /* I is false */ }
};
int main() {
foo<is_odd<201>::value>::bar();
int i = is_odd<200>::value;
}
но они также используются рекурсивно в качестве локального хранилища:
template <int N>
struct factorial {
enum {
// This enum is *not* compatible with the one in N-1
value = N * factorial<N - 1>::value
};
};
template <>
struct factorial<0> {
enum { value = 1 };
};
Это является частью причины, по которой enum class
требовался, чтобы ввести неразрывный способ добавления безопасности типа в текущее состояние enum
в С++. Было бы так много предупреждений из существующего кода, что предупреждение будет рядом с бесполезным из-за таких вещей.
Даже в довольно простом заявлении оператора switch, которое вы показали, что-то вроде этого законно:
#include <iostream>
enum Color { Green = 0x1, Red = 0x2 };
enum Fruit { Banana = 0x3 };
int main() {
int v = Green|Red;
Color c = Color(v);
switch (c) {
case Banana:
std::cerr << "Banana" << std::endl;
break;
}
return 0;
}
Хотя это законно здесь, это не очень значимо, но такие вещи используются довольно регулярно и значимо в "бит-скрипичном" C-коде еще. Точка этого примера состоит в том, что, разрешая преобразование int
↔ enum
в любом месте, это эффективно означает, что строгое описание типа enum
позже становится бессмысленным. В общем случае вы не можете определить, произошло ли подобное преобразование (возможно, это было в другой единицы перевода).
enum class
, безусловно, является самым лучшим способом введения такой строгости без ущерба для существующего кода.