enum X : int
(С #) или enum class X : int
(C ++ , 11) представляет собой тип , который имеет скрытое внутреннее поле , int
который может содержать любое значение. Кроме того, X
в перечислении определен ряд предопределенных констант . Можно привести перечисление к его целочисленному значению и наоборот. Это все верно как в C #, так и в C ++ 11.
В C # перечисления используются не только для хранения отдельных значений, но и для хранения побитовых комбинаций флагов в соответствии с рекомендацией Microsoft . Такие перечисления (обычно, но не обязательно) украшены [Flags]
атрибутом. Чтобы упростить жизнь разработчикам, побитовые операторы (OR, AND и т. Д.) Перегружены, так что вы можете легко сделать что-то вроде этого (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Я опытный разработчик C #, но программирую на C ++ только пару дней, и я не знаком с соглашениями C ++. Я намерен использовать перечисление C ++ 11 точно так же, как я это делал в C #. В C ++ 11 побитовые операторы в перечисленных областях не перегружены, поэтому я хотел их перегрузить .
Это вызвало дебаты, и мнения, кажется, различаются между тремя вариантами:
Переменная типа enum используется для хранения битового поля, аналогично C #:
void M(NumericType flags); // With operator overloading: M(NumericType::Sign | NumericType::ZeroPadding); // Without operator overloading: M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
Но это противоречило бы строго типизированной философии перечисления в ограниченных перечислениях C ++ 11.
Используйте простое целое число, если вы хотите сохранить побитовую комбинацию перечислений:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Но это сведет все к a
int
, оставляя вас без понятия, какой тип вы должны использовать в методе.Напишите отдельный класс, который будет перегружать операторы и содержать побитовые флаги в скрытом целочисленном поле:
class NumericTypeFlags { unsigned flags_; public: NumericTypeFlags () : flags_(0) {} NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} //...define BITWISE test/set operations }; void M(NumericTypeFlags flags); M(NumericType::Sign | NumericType::ZeroPadding);
( Полный код по user315052 )
Но тогда у вас нет IntelliSense или какой-либо другой поддержки, которая намекает вам на возможные значения.
Я знаю, что это субъективный вопрос , но: какой подход я должен использовать? Какой подход, если таковой имеется, наиболее широко известен в C ++? Какой подход вы используете при работе с битовыми полями и почему ?
Конечно, поскольку все три подхода работают, я ищу фактические и технические причины, общепринятые соглашения, а не просто личные предпочтения.
Например, из-за моего фона C # я склоняюсь к подходу 1 в C ++. Это дает дополнительное преимущество, заключающееся в том, что моя среда разработки может подсказывать мне возможные значения, а с перегруженными операторами enum это легко написать и понять, и довольно чисто. И подпись метода ясно показывает, какую ценность он ожидает. Но большинство людей здесь не согласны со мной, вероятно, по уважительной причине.
enum E { A = 1, B = 2, C = 4, };
, диапазон составляет 0..7
(3 бита). Таким образом, стандарт C ++ явно гарантирует, что # 1 всегда будет жизнеспособным вариантом. [В частности, по enum class
умолчанию используется, enum class : int
если не указано иное, и, следовательно, всегда имеет фиксированный базовый тип.])