【问题标题】:"enum class" emulation or solid alternative for MSVC 10.0MSVC 10.0 的“枚举类”仿真或可靠替代方案
【发布时间】:2011-06-26 04:50:36
【问题描述】:

我正在为以下问题寻找一种 hacky 的解决方案: GCC 4.4+ 接受以下 c++0x 代码:

enum class my_enum
{
    value1,
    value2
};

允许这样使用:

my_enum e = my_enum::value1;

这带来了所有的花里胡哨。我想让这段代码与 MSVC 2010 兼容,大意是使用语法不会改变。我在here 之前已经对此进行了思考,并且接受的答案有效,但是对于枚举和枚举值的两个不同名称的需求正在扼杀这两种方法的兼容性。这使得按原样替换 C++0x 代码当然无法使用。我想知道是否有一些 #undef#define 诡计可以解决这个问题,允许我使用 enum class-like 语法(也许没有严格的类型安全等),但至少相同句法。谢谢!

【问题讨论】:

    标签: c++ class visual-c++ enums c++11


    【解决方案1】:

    我刚刚发现了 James 的好 hack(我之前一直在使用)的一个问题,并解决了这个问题。当我尝试为 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 不同。这是我想出的对 James hack 的破解。

    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);
    

    注意事项:

    1. 除非 enum-base 另有指定,否则作用域枚举的基础类型是 int
    2. 允许与基础整数类型进行显式转换。但隐式转换不是。尽力而为。
    3. 这个 hack 比 James 更像是一个 pita,因为需要枚举值两次。我希望不支持作用域枚举的编译器迅速消失!

    【讨论】:

    • 这种方法似乎不允许switch/case,至少在 MSVC 10 上(我得到“C2051:case expression not constant”)。这是一个很大的 PITA,因为 enums 通常以这种方式使用。
    • @AlexandreC.:我认为这可以通过在类定义中声明伪枚举器而不是在类定义之外定义时初始化它们来解决。
    • 虽然这个 hack 相当不错,但它 正如 Alexandre 所说的那样,它是一个大 PITA,并且看起来几乎不可能以人类可写的方式与 C++11 兼容 /没有额外的预处理器。相反,我使用 Wikibooks 中的 TypeSafeEnum 习语加上一些内置宏,通过 enable_if 禁用一些常见的 enumoperations(比较、!negation 和流),这是我见过的最接近“人道”的东西禁用 enum隐式转换(这似乎是不可能的)。
    【解决方案2】:

    不要使用此解决方案。请参阅 Howard 接受的答案以获得更好的解决方案。我把这篇文章留在这里是因为霍华德的回答提到了它。

    如果您需要能够使用尚不支持新的、尚未标准或尚未广泛实施的语言功能的编译器来编译您的代码,最好避免在您的代码中使用该语言功能。

    也就是说,作为一种解决方法,您可以将 enum 包装在 struct 中并使用一对隐式转换:

    struct my_enum {
        enum type { 
            value1, 
            value2 
        };
    
        my_enum(type v) : value_(v) { }
    
        operator type() const { return value_; }
    
    private:
    
        type value_;
    };
    

    【讨论】:

    • 这是一种模式吗?为什么你认为它是黑客行为?很整洁。
    • @Inverse:如果您反复使用它,它将成为一种模式:-)。将枚举包装在结构或命名空间中以防止命名空间污染是一种常见的技术,也是我一贯使用的技术(嗯,大部分情况下)。使用隐式转换来允许像枚举一样使用封装结构并不是一种常见的模式,至少在我有幸阅读的任何代码中都没有。由于隐含转换,我对此持谨慎态度:我不断发现隐式转换允许您编写巧妙破坏的代码的不同方式...
    【解决方案3】:

    我已经为找到一个真正的最佳解决方案而奋斗了一整天,但似乎没有一个。我需要我的枚举

    1. 不能隐式转换为整数类型
    2. 可用于switch 语句
    3. 可用作非类型模板参数

    在 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.");
        }
    }
    

    不是特别好,但我想这已经很好了......

    【讨论】:

      猜你喜欢
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 1970-01-01
      • 1970-01-01
      • 2016-12-15
      相关资源
      最近更新 更多