我有这个“更好”的枚举类
如下:
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
我通过抛出异常解决了这个问题,但我更喜欢这样的代码根本不编译(编译时错误)。有没有办法管理这个?
答案 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,因为使用未初始化的值应该事先可以预防,因此属于后一类。