【发布时间】:2012-10-19 10:26:59
【问题描述】:
我在用 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:
在Implementation of N4428 enum reflection in standard C++11 (partial) 上有一个有趣的讨论,以提出更高级的枚举建议。
更新 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 之后,我发现了另一个使用枚举类的有趣模式。如果枚举类的枚举数列表是连续整数,通过定义它的最大值和最小值,我们可以检查一个值是否属于它。
如果在Player::State 上使用Count() 方法的目的是检查一个值是否在枚举中,那么使用 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在某些情况下是关键字的词重合!
【问题讨论】:
-
你不需要在 C++ 中
typedefenums。直接说enum PossibleValues { ... }。 -
建议 C++14 的人考虑这个问题不是一个好主意吗?