防止在C 中枚举枚举
Preventing casting ints to enums in C++
假设我们有
enum class Month {jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec};
每个值是一个int,0到11。然后,我希望类型月的变量仅保存这些枚举值。因此,这是创建变量的唯一确定方法:
Month m = Month::may;
但是,这是语言允许的其他一些方法:
Month m1 = Month(12345);
Month m2 = static_cast<Month>(12345);
这有些令人失望。我只允许第一种方式?或人们如何应对C 中的不良枚举?
我只允许第一种方式?
用枚举不可能。
如果您想要一个不能从(可能是无效的(值中明确转换的白痴"枚举",则可以使用完整的吹式类代替枚举。不幸的是涉及一些样板:
struct Month {
constexpr int value() noexcept { return v; }
static constexpr Month jan() noexcept { return 0; };
static constexpr Month feb() noexcept { return 1; };
static constexpr Month mar() noexcept { return 2; };
static constexpr Month apr() noexcept { return 3; };
static constexpr Month may() noexcept { return 4; };
static constexpr Month jun() noexcept { return 5; };
static constexpr Month jul() noexcept { return 6; };
static constexpr Month aug() noexcept { return 7; };
static constexpr Month sep() noexcept { return 8; };
static constexpr Month oct() noexcept { return 9; };
static constexpr Month nov() noexcept { return 10; };
static constexpr Month dec() noexcept { return 11; };
private:
int v;
constexpr Month(int v) noexcept: v(v) {}
};
可以通过使用封装常规枚举的常规类可以解决您的问题,例如在以下示例中:
class Month {
public:
enum Type {
jan, feb, mar, apr, may, jun,
jul, aug, sep, oct, nov, dec
};
Month(Type t);
private :
Type type;
};
然后以下会产生编译时错误:
Month mm = Month::jan;
Month m1 = Month(12345);
Month m2 = static_cast<Month>(12345);
e.cpp:27:25: error: invalid conversion from 'int' to 'Month::Type' [-
fpermissive]
Month m1 = Month(12345);
e.cpp:26:42: error: invalid conversion from 'int' to 'Month::Type' [-
fpermissive]
Month m2 = static_cast<Month>(12345);
仍然有一个可能的,但更复杂的方案
Month m1 = Month(Month::Type(12345));
但是,可以动态检查一下
Month::Month(Type t) : type(t){
if (int(t) < 0 || int(t) > int(dec)) {
throw "error";
}
}
然后,我期望类型月的变量仅容纳这些枚举值
然后,您误解了枚举(甚至是范围的枚举(。它们为您提供了基本类型的一些值的方便名称(并且,在范围内的枚举的情况下,禁止从该类型的隐式转换(。他们不将对象限制为那些命名值,也不是打算的。如果您想这样做,请在更改其状态的任何事物中创建一个具有验证例程的类。
但是,这种方法的开销通常不值得。遵循通常的C 练习,只是假定您永远不会给枚举一个您不应该的价值。如果你这样做,为什么?那是一个错误:修复它。范围内的枚举提供的隐式转换的禁令应该使这些错误消失了。如果有人竭尽全力明确转换一个未命名的价值?那是他们自己的错!记录程序的行为将"未定义"(不是根据语言而是根据您自己的代码的API(并继续。
您不能禁止在不修改语言本身的情况下允许语言允许的内容。毕竟,这是完全有可能的:
Month m;
int * val = (int *) &m;
*val = -46;
...没有什么可以阻止您的。关键是人们通常不应该这样做,如果这样做,他们通常有很好的理由。
如果您想执行更强大的编码策略,则需要一个支持该类型诊断的编译器(通常是通过警告标志,通常可以转换为硬错误(,或者仅仅将其简单地转换为其他语言。如果这是不可接受的,通常人们只是不像我发布的示例那样编写疯狂的代码来应对这样的问题。
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 错误:从"int"到枚举c++的转换无效
- C++中构造函数中的枚举
- 访问在 C++ 结构中声明的枚举变量
- 枚举类'classname'的多重定义
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- typedef 枚举和枚举类有什么区别?
- 为什么我的开关/机箱在使用枚举时默认?
- 标准::可选枚举的比较运算符
- C++两个源文件之间共享的枚举的静态实例
- 打印没有铸件的枚举可以在C++中吗?
- 枚举成员与静态 int 成员?
- C++:枚举:错误:应使用标识符而不是"}"
- 带有 c++ 的枚举(输入检查)
- 在 qml 中使用 Q_ENUM 和 Q_PROPERTY 作为枚举类
- 为什么 int 类型的枚举类值不能用作 int
- 在 C++ 中输出枚举类类型的向量元素