Базовый тип перечисления C ++ в C ++ 0x - PullRequest
27 голосов
/ 12 мая 2009

Я пытался прочитать немного стандарта C ++, чтобы понять, как работает 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;
}

Ответы [ 5 ]

16 голосов
/ 12 мая 2009

enum class - это функция C ++ 0x , отсутствует в C ++ 03.

В стандартном C ++ перечисления не являются типобезопасными. Они являются целыми числами, даже когда типы перечисления различны. Это позволяет сравнивать два значения перечисления разных типов перечисления. Единственная безопасность, которую обеспечивает C ++ 03, заключается в том, что целое число или значение одного типа перечисления не преобразуется неявно в другой тип перечисления. Кроме того, базовый целочисленный тип, размер целого числа, не может быть указан явно; это определенная реализация. И, наконец, значения перечисления ограничиваются областью применения. Таким образом, два отдельных перечисления не могут иметь совпадающие имена членов. C ++ 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.

Что касается части сериализации (которая, я думаю, не была частью исходного вопроса), я предпочитаю создавать вспомогательный класс, который переводит элементы enum в их строковый эквивалент (и обратно), поскольку имена обычно более стабильны, чем целочисленные значения, которые они представляют, поскольку элементы enum могут (а иногда и) переупорядочиваться без изменения поведения кода.

14 голосов
/ 12 мая 2009

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

Это не представляет особого интереса для этой проблемы сериализации (поскольку размер сериализованных данных не имеет постоянной ширины, и это может вызвать проблемы при изменении перечисления и его базового типа), но обычно интересно выяснить тип, хранящий все перечисление. Ура!

5 голосов
/ 11 июля 2012
#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;
5 голосов
/ 12 мая 2009

Я не читал никаких материалов по 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
1 голос
/ 12 мая 2009

Что касается enum class color: это код C ++ / CLI (C ++ .NET) или будущий код C ++ 0x?

Для сериализации вы можете получить размер перечисления с помощью sizeof(color), чтобы узнать, сколько байтов нужно скопировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...