An enum X : int
(C #) або enum class X : int
(C ++ 11) - це тип, який має приховане внутрішнє поле, int
яке може містити будь-яке значення. Крім того, на перелік визначено ряд попередньо визначених констант X
. Можна привести enum до його цілого значення і навпаки. Це все вірно і в C #, і в C ++ 11.
У C # перерахунки використовуються не лише для зберігання окремих значень, але й для зберігання побітових комбінацій прапорів, згідно рекомендацій Microsoft . Такі перерахунки (як правило, але не обов'язково) прикрашені [Flags]
атрибутом. Щоб полегшити життя розробників, побітні оператори (АБО, І т.д. тощо) перевантажуються, так що ви можете легко зробити щось подібне (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));
Але це зменшить усе до рівня
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 # я схиляюся з підходом 1 в C ++. Це має додаткову перевагу, що моє середовище розробки може натякати мені на можливі значення, а з перевантаженими операторами enum це легко написати і зрозуміти, і досить чисто. І підпис методу чітко показує, яке значення він очікує. Але більшість людей тут не згодні зі мною, напевно, з поважних причин.
enum E { A = 1, B = 2, C = 4, };
діапазону 0..7
(3 біта). Таким чином, стандарт C ++ явно гарантує, що №1 завжди буде життєздатним варіантом. [Зокрема, enum class
за замовчуванням, enum class : int
якщо не вказано інше, і, таким чином, завжди є фіксований базовий тип.])