Подтвердить что ты не робот

Основной тип перечисления С++ в С++ 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;
}
4b9b3361

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