Я лично использую адаптированную версию typesafe enum idiom . Он не содержит всех пяти «требований», которые вы указали в своем редактировании, но я все равно категорически не согласен с некоторыми из них. Например, я не вижу, как Prio # 4 (преобразование значений в строки) имеет какое-либо отношение к безопасности типов. Как бы то ни было, большую часть временного представления отдельных значений следует отделять от определения типа (подумайте о i18n по простой причине). Prio # 5 (итерация, которая является необязательной) - одна из самых приятных вещей, которые я хотел бы видеть естественно , происходящих в перечислениях, поэтому мне было грустно, что это выглядит как «необязательное» в вашем запросе, но это кажется, что он лучше решается через отдельную итерационную систему , такую как begin
/ end
функции или enum_iterator, что позволяет им беспрепятственно работать с STL и C ++ 11 foreach.
OTOH эта простая идиома прекрасно предоставляет Prio # 3 Prio # 1 благодаря тому, что она в основном только оборачивает enum
s дополнительной информацией о типах. Не говоря уже о том, что это очень простое решение, которое по большей части не требует каких-либо внешних заголовков зависимостей, поэтому его довольно легко переносить. Он также имеет преимущество, заключающееся в том, что перечисления ограничены a-la-C ++ 11:
// This doesn't compile, and if it did it wouldn't work anyway
enum colors { salmon, .... };
enum fishes { salmon, .... };
// This, however, works seamlessly.
struct colors_def { enum type { salmon, .... }; };
struct fishes_def { enum type { salmon, .... }; };
typedef typesafe_enum<colors_def> colors;
typedef typesafe_enum<fishes_def> fishes;
Единственная «дыра», которую обеспечивает решение, заключается в том, что оно не учитывает тот факт, что оно не препятствует прямому сравнению enum
s различных типов (или enum
и int), потому что когда вы используете значения напрямую, вы принудительно неявное преобразование в int
:
if (colors::salmon == fishes::salmon) { .../* Ooops! */... }
Но до сих пор я обнаружил, что такие проблемы можно решить, просто предложив лучшее сравнение с компилятором - например, явно предоставив оператор, который сравнивает любые два различных типа enum
, а затем принудительно завершив его:
// I'm using backports of C++11 utilities like static_assert and enable_if
template <typename Enum1, typename Enum2>
typename enable_if< (is_enum<Enum1>::value && is_enum<Enum2>::value) && (false == is_same<Enum1,Enum2>::value) , bool >
::type operator== (Enum1, Enum2) {
static_assert (false, "Comparing enumerations of different types!");
}
Хотя кажется, что пока он не нарушает код и явно решает конкретную проблему, не занимаясь чем-то другим, я не уверен, что это такая вещь " должна msgstr "делать (я подозреваю, что это будет мешать enum
s, уже участвующим в операторах преобразования, объявленных в другом месте; я с удовольствием получу комментарий об этом).
Сочетание этого с вышеупомянутой типизированной идиомой дает что-то, что сравнительно близко к C ++ 11 enum class
в удобочитаемости (удобочитаемость и ремонтопригодность) без необходимости делать что-то слишком неясное. И я должен признать, что это было забавно, я никогда не думал спросить компилятор, имел ли я дело с enum
с или нет ...