如何在C ++中编译转换为枚举类型?

时间:2011-01-18 15:08:09

标签: c++ enums casting

我有这个“更好”的枚举类

  1. 不能包含无效值,
  2. 在枚举值未明确设置之前无法使用
  3. 如下:

    class Symmetry
    {
        public:
            enum Type {
                GENERAL, SYMMETRIC, HERMITIAN,
                SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED
            };
    
            Symmetry() { t_ = UNINITIALIZED }
            explicit Symmetry(Type t) : t_(t) { checkArg(t); }
            Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; }
    
            operator Type() const {
                if (t_ == UNINITIALIZED) throw runtime_error("error");
                return t_;
            }
    
        private:
            Type t_;
    
            void checkArg(Type t) {
                if ((unsigned)t >= (unsigned)UNINITIALIZED)
                    throw runtime_error("error");
            }
    }; 
    

    这允许我编写以下代码:

    Symmetry s1(Symmetry::SYMMETRIC);
    Symmetry s2;
    s2 = Symmetry::HERMITIAN;
    Symmetry s3;
    if (Symmetry::GENERAL == s3) // throws
    

    我的问题是编译器允许构造如:

    Symmetry s1((Symmetry::Type)18); // throws
    Symmetry s2;
    s2 = (Symmetry::Type)18; // throws
    

    我通过抛出异常解决了这个问题,但我更喜欢这样的代码根本不编译(编译时错误)。有没有办法管理这个?

3 个答案:

答案 0 :(得分:4)

可能是一个糟糕的解决方案,但它可以解决您当前的问题。不要使用内部枚举类型,而是使用私有构造函数定义一个小助手类,并使外部类成为朋友。然后“enum”值可以是外部类中的静态const成员。像这样:

免责声明:未经测试,因此可能存在各种编译问题,但您应该明白

class Symmetry
{
public:
    class Type
    {
    private:
        Type() {};
        friend class Symmetry;
    };

    static const Type GENERAL;
    static const Type SYMMETRIC;
    static const Type HERMITIAN;
};

你需要某种方法来确定平等,但这应该相当容易。

答案 1 :(得分:1)

我尝试使用模板:(已测试。但是,这可以进一步改进!)

template<int N>
struct Symmetry  
{
    enum Type
    {
        GENERAL, SYMMETRIC, HERMITIAN,
        SKEW_SYMMETRIC, SKEW_HERMITIAN
    };
    template<Type e> struct allowed;
    template<> struct allowed<GENERAL>        { static const int value = GENERAL; };
    template<> struct allowed<SYMMETRIC>      { static const int value = SYMMETRIC; };
    template<> struct allowed<HERMITIAN>      { static const int value = HERMITIAN; };
    template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; };
    template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; };

    allowed<(Type)N> m_allowed;

    operator int()
    {
        return N;
    }
};

Symmetry<0> e0;                   //okay
Symmetry<1> e1;                   //okay
Symmetry<100> e4;                 //compilation error!
Symmetry<e0.SKEW_HERMITIAN> e3;   //okay
Symmetry<e0.SKEW_SYMMETRIC> e3;   //okay

用法:

int main()
{
    Symmetry<0> e0;                   
    Symmetry<e0.HERMITIAN> e1;           

    switch (e1)
    {
    case e0.HERMITIAN:
        {
            cout << "It's working" << endl;
        }
        break;
    }
}

答案 2 :(得分:0)

没有。如果您允许使用任何强制转换,正如您的上一个示例所做的那样,那么将始终存在一些可用于破坏您的类型的强制转换。

解决方案是不习惯使用这些演员,并且非常怀疑地考虑任何使用这些演员阵容的代码。将这种类型的铸件视为你的武器库中的核弹:它很重要,但是你总是小心翼翼地处理它,并且永远不要比很少部署它。

您的编译器有什么警告选项可用于投射?您使用哪种lint工具可以检测到这种滥用的演员?


也就是说,看起来你真的想要隐藏内部类型,因此用户不太愿意使用它。意识到这是straight-forward to make that type name private,即使在没有阻止所有演员滥用的情况下,通过略微调整你的原作:

struct Symmetry {
  enum {
    UNINITIALIZED,
    GENERAL, SYMMETRIC, HERMITIAN,
    SKEW_SYMMETRIC, SKEW_HERMITIAN
  };

private:
  typedef decltype(UNINITIALIZED) Hidden;
  Hidden _value;

public:
  Symmetry(Hidden value = UNINITIALIZED) : _value (value) {}
  Symmetry& operator=(Hidden value) { _value = value; return *this; }
  operator Hidden() const {
    if (_value == UNINITIALIZED) {
      throw std::logic_error("uninitialized Symmetry");
    }
    return _value;
  }

  bool initialized() const { return _value != UNINITIALIZED; }
  // required if you want to check for UNINITIALIZED without throwing in
  // the above conversion
};

这是一个完整的实现,没有遗漏或未知的细节,或初始化顺序的问题。唯一需要注意的是decltype - 使用前C ++ 0x编译器,你必须使用特定于实现的东西或者library来包装特定于实现的东西。

一个较小的问题:从runtime_error更改为logic_error,因为使用未初始化的值应该事先可以预防,因此属于后一类。