Основной тип перечисления С++ в С++ 0x
Я пытался прочитать немного стандарта С++, чтобы выяснить, как работает enum. На самом деле там больше, чем я думал.
Для области перечисления в области видимости ясно, что базовый тип 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;
}
Ответы
Ответ 1
Я не читал никаких файлов С++ 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
Ответ 2
enum class является C++0x, его нет в С++ 03.
В стандартном С++ перечисления не являются безопасными для типов. Они представляют собой целые числа, даже если типы перечисления различны. Это позволяет сравнивать между двумя значениями перечисления различных типов перечислений. Единственная безопасность, которую предоставляет С++ 03, состоит в том, что целое число или значение одного типа перечисления не преобразуются неявно в другой тип перечисления. Кроме того, не может быть явно задан базовый интегральный тип - размер целого; это реализация определена. Наконец, значения перечисления привязаны к охватывающей области. Таким образом, невозможно, чтобы две отдельные перечисления имели совпадающие имена элементов. С++ 0x допускает специальную классификацию перечисления, которая не имеет ни одной из этих проблем. Это выражается с помощью объявления класса enum
Примеры (из статьи в Википедии):
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.
Что касается части сериализации (которая, как я думаю, не была частью исходного вопроса), я предпочитаю создавать вспомогательный класс, который переводит элементы перечисления в их эквивалент строки (и обратно), поскольку имена обычно более стабильны, чем целочисленные значения, которые они представляют, поскольку элементы перечисления могут (и иногда) переупорядочиваться без изменения поведения кода.
Ответ 3
Я решил создать новый ответ, потому что мой старый был настолько грязным. В любом случае просто хочу сказать что-то о С++ 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
Это не представляет особого интереса к этой проблеме сериализации (поскольку размер сериализованных данных не имеет постоянной ширины, и это может вызвать проблемы при изменении перечисления и его базового типа), но, как правило, интересно выяснить тип, хранящий все перечисление. Ура!
Ответ 4
#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 an int!" << endl;
else if (std::is_same<underlying_type, unsigned int>::value)
std::cout << "It an uint!" << endl;
Ответ 5
Что касается enum class color
: Является ли этот С++/CLI (С++.NET) или будущим кодом С++ 0x?
Для сериализации вы можете получить размер перечисления с помощью sizeof(color)
, чтобы узнать, сколько байтов нужно скопировать.