我在用 C++ 编写枚举时遇到了一种模式。它是这样的:
class Player
{
public:
class State
{
public:
typedef enum
{
Stopped,
Playing,
Paused
}PossibleValues;
static const int Count() {return Paused+1;};
static const PossibleValues Default() {return Stopped;};
};
//...
}
这解决了枚举的一些常见问题,例如外部命名空间的污染等。但仍有一件事我不喜欢:Count() 是手动完成的。我知道的方法只有两种:这个是从 Last+1 计算出来的;或编写普通的硬编码。
问题是:是否有某种方法,例如使用预处理器宏,可以自动获取计数,并将其放在 Count() 方法中?注意:我不想在枚举中添加最后一个名为 Count 的假元素,污染它!
提前致谢!
更新 1:
有一个关于在标准 C++11(部分)中实现 N4428 枚举反射的有趣讨论,以提出更高级的枚举的建议。
更新 2:
有趣的文档N4451-Static reflection (rev. 3)关于 MetaEnums 和 MetaEnumClasses 的第 3.16、3.17、A.7、A.8 节。
更新 3:
在我看到https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962之后,我使用枚举类来到了另一个有趣的模式。如果枚举类的枚举数列表是连续整数,通过定义它的最大值和最小值,我们可以检查一个值是否属于它。
如果使用该Count()
方法的目的Player::State
是检查一个值是否在枚举中,那么该目的也可以通过 numeric_limits 方法实现,甚至更好,因为它不需要枚举器列表以零值项开头!
enum class Drink
{
Water,
Beer,
Wine,
Juice,
};
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
namespace std
{
template <> class numeric_limits < Drink >
{
public:
static const/*expr*/ bool is_specialized = true;
static const/*expr*/ Drink min() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink max() /*noexcept*/ { return Drink::Juice; }
static const/*expr*/ Drink lowest() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink default() /*noexcept*/ { return Drink::Beer; }
};
}
#pragma pop_macro("min")
#pragma pop_macro("max")
使用案例:
来自应用程序的变量:
Drink m_drink;
在构造函数中初始化为:
m_drink = numeric_limits<Drink>::default();
在表单的初始化上,我可以这样做:
pComboDrink->SetCurSel(static_cast<int>(theApp.m_drink));
在它上面,为了使界面适应用户所做的更改,我可以使用作用域枚举类值进行切换:
switch (static_cast<Drink>(pComboDrink->GetCurSel()))
{
case Drink::Water:
case Drink::Juice:
pAlcohoolDegreesControl->Hide();
break;
case Drink::Beer:
case Drink::Wine:
pAlcohoolDegreesControl->Show();
break;
default:
break;
}
在对话框的确认过程OnOK
(
int ix= pComboDrink->GetCurSel();
if (ix == -1)
return FALSE;
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
if (ix < static_cast<int> (std::numeric_limits<Drink>::min()) || ix > static_cast<int> (std::numeric_limits<Drink>::max()) )
return FALSE;
#pragma pop_macro("min")
#pragma pop_macro("max")
theApp.m_drink= static_cast<Drink>(ix);
笔记:
- 关键字
constexpr
(我注释/*expr*/
了,将其保留为const
)并且noexcept
仅因为我使用的编译器(Visual C++ 2013)在当前版本不支持它们而被注释。 - 也许您不需要逻辑来临时取消定义 min 和 max 宏。
- 我知道这
default()
不适合“数字限制”范围;但它似乎是一个方便的地方。default
甚至它与在某些情况下是关键字的词重合!