Ответ 1
Короткий ответ - да, это жизнеспособное решение (с одним исправлением).
Вот длинный ответ.:)
У вас есть ошибка времени компиляции с вашими функциями сравнения, строго говоря. Это вызовет проблемы с переносимостью со стандартными компиляторами. В частности, рассмотрим следующее:
bool foo(Klingon e) { return e == Klingon::Qapla }
Компилятор не должен знать, какую перегрузку использовать operator==
, как неявно (operator type() const
), так и преобразование e
в KlingonType::type
и неявное преобразование Klingon::Qapla
в Klingon
(через Klingon(type)
) требуется одно преобразование.
Требование operator type() const
быть explicit
исправляет эту ошибку. Конечно, explicit
не существует в С++ 03. Это означает, что вам нужно будет сделать, как @Yakk предлагает в комментариях и использовать что-то похожее на idiom safe-bool для типа inner
. Удаление operator type() const
полностью не является вариантом, потому что оно будет удалять явные преобразования в интегральные типы.
Поскольку вы говорите, что все в порядке с неявными преобразованиями, которые по-прежнему возможны, более простым решением будет также определение функций сравнения с базовым типом enum
. Так что в дополнение к:
friend bool operator == (const like_enum & lhs, const like_enum & rhs) { return lhs.val == rhs.val; }
friend bool operator != (const like_enum & lhs, const like_enum & rhs) { return lhs.val != rhs.val; }
friend bool operator < (const like_enum & lhs, const like_enum & rhs) { return lhs.val < rhs.val; }
friend bool operator <= (const like_enum & lhs, const like_enum & rhs) { return lhs.val <= rhs.val; }
friend bool operator > (const like_enum & lhs, const like_enum & rhs) { return lhs.val > rhs.val; }
friend bool operator >= (const like_enum & lhs, const like_enum & rhs) { return lhs.val >= rhs.val; }
вам также понадобится:
friend bool operator ==(const like_enum& lhs, const type rhs) { return lhs.val == rhs; }
friend bool operator !=(const like_enum& lhs, const type rhs) { return lhs.val != rhs; }
friend bool operator < (const like_enum& lhs, const type rhs) { return lhs.val < rhs; }
friend bool operator <=(const like_enum& lhs, const type rhs) { return lhs.val <= rhs; }
friend bool operator > (const like_enum& lhs, const type rhs) { return lhs.val > rhs; }
friend bool operator >=(const like_enum& lhs, const type rhs) { return lhs.val >= rhs; }
friend bool operator ==(const type lhs, const like_enum& rhs) { return operator==(rhs, lhs); }
friend bool operator !=(const type lhs, const like_enum& rhs) { return operator!=(rhs, lhs); }
friend bool operator < (const type lhs, const like_enum& rhs) { return operator> (rhs, lhs); }
friend bool operator <=(const type lhs, const like_enum& rhs) { return operator>=(rhs, lhs); }
friend bool operator > (const type lhs, const like_enum& rhs) { return operator< (rhs, lhs); }
friend bool operator >=(const type lhs, const like_enum& rhs) { return operator<=(rhs, lhs); }
После исправления вышеизложенного существует незначительная заметная разница семантически (игнорирование невозможных преобразований). Единственное различие, которое я нашел, это значение std::is_pod<Klingon>::value
из <type_traits>
в С++ 11. Используя версию С++ 03, это будет false
, тогда как при использовании enum class
es это будет true
. На практике это означает (без оптимизации), что a Klingon
с использованием enum class
может переноситься в регистре, тогда как версия like_enum
должна быть в стеке.
Поскольку вы не укажете базовое представление enum class
, sizeof(Klingon)
, вероятно, будет одинаковым для обоих, но я бы не стал полагаться на него. Ненадежность базового представления, выбранного разными реализациями, была частью мотивации строго типизированного enum
в конце концов.
Здесь доказательство вышеупомянутых двух абзацев для clang++ 3.0+, g++ 4.5+ и msvc 11 +.
Теперь с точки зрения скомпилированного вывода оба явно будут иметь несовместимый ABI. Это означает, что вся ваша кодовая база должна использовать либо одну, либо другую. Они не будут смешиваться. Для моей системы (clang++ - 3.5 в OSX) приведенный выше символ функции __Z1f9like_enumI11KlingonTypeNS0_4typeEE
для версии С++ 03 и __Z1f7Klingon
для версии С++ 11. Это должно быть проблемой только в том случае, если они экспортируют библиотечные функции.
Выбранная сборка идентична в моем тестировании для clang++ и g++ после поворота оптимизаций на -O2
. Предположительно другие оптимизирующие компиляторы также смогут развернуть Klingon
до KlingonType::type
. Без оптимизаций версия enum class
все равно будет, конечно, избегать всех вызовов функции конструктора и оператора сравнения.