Вопрос

Я пытался немного прочитать стандарт 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) чтобы узнать, сколько байтов копировать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top