在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),其3.16、3.17、A.7、A.8节中有关MetaEnums和MetaEnumClasses的内容。
- 更新3:**
I came to another interesting pattern using an enum class, after I've seen https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962. If the enum class'senumerator list is continuously integer, by defining its maximum and its minimum, we can check whether a value belongs to it or not.
如果在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
)中,我可以在将值保存到相应的app var之前检查值是否超出边界:
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);
注:
1.关键字constexpr
(我注解了/*expr*/
,保留为const
)和noexcept
被注解,只是因为我使用的编译器(Visual C++2013)在当前版本中还不支持它们。
1.也许您不需要临时取消定义最小和最大宏的逻辑。
1.我知道default()
不适合"数字限制"范围;但它似乎是一个方便的地方把它;甚至它与在某些上下文中是关键字的default
单词一致!
5条答案
按热度按时间kokeuurv1#
不,没有,如果你需要这个,你可能一开始就不应该使用
enum
。在您的特定情况下,您希望调用
Count
的用例是什么?vmjh9lq92#
AFAIK没有自动编译器支持的关键字来获取
enum
中元素的总数。OTOH这通常没有意义:您可以有多个值具有相同的值,只要这些值不必具有后续值(即您可以手动分配值,而不是依赖于自动编号)。一种常见的做法是按以下方式声明
enum
:这样,如果
count
总是最后定义的-它将给予你枚举元素的计数,假设编号从0开始并且是结果。ipakzgxi3#
重新发布一个类似问题的答案(什么是非序列整数c++枚举的最佳方法),因为它与一个几乎没有答案的问题有关。
一种可以用来获取所需内容的模式是使用std::initializer_list来存储枚举的所有值。
这样做的好处还在于,即使枚举的值不是线性值,也能够对其进行迭代。
我认为你可以同时生成枚举,初始化器列表和函数,从一个带有变量的宏中,尽管在最好的情况下,这种东西应该在标准中。
编辑:当我使用PossibleValues作为枚举或者使用一个struct作为PossibleValues时,我的编译器会抱怨类型不完整。使用命名空间作为枚举有点不寻常,但是它工作得很好。
juud5qan4#
https://stackoverflow.com/a/60216003/12894563的求解方法可以改进,可以将枚举表达式保存在静态向量中,进行迭代,求最小值/最大值等
用法:
khbbv19g5#
PossibleValues的类型必须是枚举吗?如果您只需要行为类似枚举的东西,可以执行以下操作:
我有点纠结于这个解决方案。一方面,它很一般,另一方面,它是一个小问题的大锤。