"эмуляция класса enum" или твердая альтернатива для MSVC 10.0
Я ищу хакерский вид решения следующей проблемы:
GCC 4.4+ принимает следующий код С++ 0x:
enum class my_enum
{
value1,
value2
};
Это позволяет использовать следующее:
my_enum e = my_enum::value1;
со всеми этими звонками и свистами. Я хотел бы сделать этот код совместимым с MSVC 2010, так как синтаксис использования не изменяется. Я уже обдумывал это перед здесь, и принятый ответ работает, но необходимость в двух разных именах для перечисления и значений перечисления убивает совместимость двух подходы. Это делает его, конечно, непригодным для замены кода С++ 0x как есть. Я задавался вопросом, могут ли некоторые трюки #undef
и #define
обойти это, позволяя мне использовать синтаксис enum class
-like (возможно, без строгого типа безопасности и т.д.), Но по крайней мере тот же синтаксис. Спасибо!
Ответы
Ответ 1
Я только что обнаружил проблему с хорошим взломом Джеймса (который я использовал до сих пор) и исправление проблемы. Я обнаружил проблему, когда попытался определить оператор потока для my_enum.
#include <iostream>
struct my_enum {
enum type {
value1,
value2
};
my_enum(type v) : value_(v) { }
operator type() const { return value_; }
private:
type value_;
};
std::ostream&
operator<<(std::ostream& os, my_enum v)
{
return os << "streaming my_enum";
}
int main()
{
std::cout << my_enum::value1 << '\n';
}
Вывод:
0
Проблема my_enum::value1
имеет другой тип, чем my_enum
. Вот взломать Джеймса, который я придумал.
struct my_enum
{
static const my_enum value1;
static const my_enum value2;
explicit my_enum(int v) : value_(v) { }
// explicit // if you have it!
operator int() const { return value_; }
private:
int value_;
};
my_enum const my_enum::value1(0);
my_enum const my_enum::value2(1);
Примечания:
- Если иное не указано базой enum, базовый тип перечисления с областью -
int
.
- Разрешены явные преобразования в базовый тип интеграла и из него. Но неявных преобразований нет. Сделайте все возможное.
- Этот хак - скорее лаваш, чем Джеймс, из-за необходимости перечислить значения дважды. Я надеюсь, что компиляторы без поддержки enum enum быстро исчезнут!
Ответ 2
Не используйте это решение. См. Принятый ответ Говарда для лучшего решения. Я оставляю этот пост здесь, потому что ответ Говарда относится к нему.
Если вам нужно скомпилировать свой код с компилятором, который еще не поддерживает новую, еще не стандартную или еще не широко используемую языковую функцию, лучше избегать использования этой языковой функции в вашем коде.
Тем не менее, как обходной способ взлома, вы можете обернуть enum
в struct
и использовать пару неявных преобразований:
struct my_enum {
enum type {
value1,
value2
};
my_enum(type v) : value_(v) { }
operator type() const { return value_; }
private:
type value_;
};
Ответ 3
Я боролся целый день, чтобы найти по-настоящему оптимальное решение, но, похоже, этого не было. Мне нужно мое перечисление, которое
- Неявно конвертируется в интегральный тип
- Используется в инструкции
switch
- Используется в качестве параметра непигового шаблона
В приложении появился следующий код, построенный на решении Howard Hinnant:
struct DataType
{
struct integral {
enum type { None, Single, Double, Int };
};
typedef typename integral::type integral_type;
explicit DataType(integral_type v) : val(v) {}
integral_type integral_value() const { return val; }
bool operator==(const DataType& s) const { return val == s.val; }
bool operator!=(const DataType& s) const { return val != s.val; }
static const DataType None;
static const DataType Single;
static const DataType Double;
static const DataType Int;
private:
integral_type val;
};
В файле .cpp
:
const DataType DataType::None (DataType::integral::None);
const DataType DataType::Single (DataType::integral::Single);
const DataType DataType::Double (DataType::integral::Double);
const DataType DataType::Int (DataType::integral::Int);
Как параметр шаблона непигового типа:
template <DataType::integral_type>
struct DataTypeTraits;
template <>
struct DataTypeTraits<DataType::integral::Single>
{
enum { size = 4 };
};
В коммутаторе:
size_t get_size(DataType type)
{
switch (type.integral_value()) {
case DataType::integral::Single: return DataTypeTraits<DataType::integral::Single>::size;
case DataType::integral::Double: return DataTypeTraits<DataType::integral::Double>::size;
case DataType::integral::Int: return DataTypeTraits<DataType::integral::Int>::size;
default: throw std::logic_error("Unknown data type.");
}
}
Не особенно замечательно, но, насколько я понимаю, я думаю...