Базовый тип перечисления C++ в C++0x
Вопрос
Я пытался немного прочитать стандарт C++, чтобы понять, как работает перечисление.На самом деле там больше, чем я изначально думал.
Для перечисления с ограниченной областью ясно, что базовым типом является int
если иное не указано в предложении enum-base (это может быть любой целочисленный тип).
enum class color { red, green, blue}; // these are int
Кажется, что для перечислений без области видимости базовым типом может быть любой целочисленный тип, который будет работать, и что он не будет больше, чем int, если только в этом нет необходимости.
enum color { red, green, blue}; // underlying type may vary
Поскольку базовый тип перечислений без области действия не стандартизирован, как лучше всего справиться с сериализацией их экземпляров?До сих пор я конвертировал в int
при записи затем сериализуется в int
и устанавливаю свой enum
переменная в переключателе при чтении, но это кажется немного неуклюжим.Есть ли способ лучше?
enum color { red, green, blue };
color c = red;
// to serialize
archive << (int)c;
// to deserialize
int i;
archive >> i;
switch(i) {
case 0: c = red; break;
case 1: c = green; break;
case 2: c = blue; break;
}
Решение
Я не читал ничего о C++0x, поэтому не могу прокомментировать это.
Что касается сериализации, вам не нужен переключатель при обратном чтении перечисления — просто приведите его к типу перечисления.
Однако я не использую кастинг при записи в поток.Это связано с тем, что мне часто нравится писать оператор<< для перечисления, чтобы я мог перехватывать записанные неверные значения или вместо этого решить записать строку.
enum color { red, green, blue };
color c = red;
// to serialize
archive << c; // Removed cast
// to deserialize
int i;
archive >> i;
c = (color)i; // Removed switch
Другие советы
класс перечисления это С++0x функция, ее нет в C++03.
В стандартном C++ перечисления не являются типобезопасными.Фактически они являются целыми числами, даже если типы перечисления различны.Это позволяет сравнивать два значения перечисления разных типов перечисления.Единственная безопасность, которую обеспечивает C++03, заключается в том, что целое число или значение одного типа перечисления не преобразуются неявно в другой тип перечисления.Кроме того, базовый целочисленный тип, размер целого числа, не может быть явно указан;это определяется реализацией.Наконец, значения перечисления ограничиваются охватывающей областью.Таким образом, два отдельных перечисления не могут иметь совпадающие имена членов.C++0x позволит использовать специальную классификацию перечислений, которая не имеет ни одной из этих проблем.Это выражается с помощью объявления класса перечисления.
Примеры (из статьи в Википедии):
enum Enum1; //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int; //Legal in C++0x.
enum class Enum3; //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short; //Illegal in C++0x, because Enum2 was previously declared with a different type.
Что касается части сериализации (которая, я думаю, не была частью исходного вопроса), я предпочитаю создать вспомогательный класс, который переводит элементы перечисления в их строковый эквивалент (и обратно), поскольку имена обычно более стабильны, чем целочисленные значения, которые они содержат. представляют, поскольку элементы перечисления могут быть переупорядочены (а иногда и переупорядочены) без изменения поведения кода.
Я решил создать новый ответ, потому что мой старый был таким грязным.В любом случае, просто хочу сказать кое-что о C++11, где вы можете получить базовый тип перечисления, используя этот:
std::underlying_type_t<E>
И ради интереса идея разрешения перегрузок.Но, пожалуйста, используйте имена для хранения перечисления, как предложил @lothar.
Разрешение перегрузки связано с тем, что существует одно повышение от перечисления до первого из int, unsigned int, long, unsigned long, которое может представлять все значения своего базового типа.Преобразование в любой другой целочисленный тип имеет более низкий рейтинг, и разрешение перегрузки не предпочтет его.
char (& f(int) )[1];
char (& f(unsigned int) )[2];
char (& f(long) )[3];
char (& f(unsigned long) )[4];
char const* names[] = {
"int", "unsigned int",
"long", "unsigned long"
};
enum a { A = INT_MIN };
enum b { B = UINT_MAX };
enum c { C = LONG_MIN };
enum d { D = ULONG_MAX };
template<typename T> void print_underlying() {
std::cout << names[sizeof(f(T()))-1] << std::endl;
}
int main() {
print_underlying<a>();
print_underlying<b>();
print_underlying<c>();
print_underlying<d>();
}
И он печатает это здесь:
int
unsigned int
int
unsigned int
Эта проблема сериализации не представляет особого интереса (поскольку размер сериализуемых данных не имеет постоянной ширины, и это может вызвать проблемы при изменении перечисления и его базового типа), но в целом интересно выяснить тип, хранящий все перечисление.Ваше здоровье!
#include <type_traits>
enum a { bla1, bla2 };
typedef typename std::underlying_type<a>::type underlying_type;
if (std::is_same<underlying_type, int>::value)
std::cout << "It's an int!" << endl;
else if (std::is_same<underlying_type, unsigned int>::value)
std::cout << "It's an uint!" << endl;
Относительно enum class color
:Это C++/CLI (C++.NET) или будущий код C++0x?
Для сериализации вы можете получить размер перечисления с помощью sizeof(color)
чтобы узнать, сколько байтов копировать.