我在用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之后,我使用枚举类来了解另一个有趣的模式。如果枚举类的枚举器列表是连续整数,通过定义其最大值和最小值,我们可以检查值是否属于它。
如果在Count()
上使用Player::State
方法的目的是检查一个值是否在枚举中,那么使用numeric_limits方法也可以实现这个目的,甚至更优越,枚举器列表不需要以ZERO值项开头!
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
)上,我可以在将值保存到相应的应用程序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);
注意:
constexpr
(我评论/*expr*/
,将其留作const
)和noexcept
仅仅因为我使用的编译器(Visual C ++ 2013)没有评论在当前版本中支持它们。default()
不符合“数字限制”范围;但它似乎是一个方便的地方穿上它;即使它与default
这个词相吻合,在某些情况下它也是一个关键词!答案 0 :(得分:4)
AFAIK没有自动编译器支持的关键字来获取enum
中的元素总数。 OTOH这通常没有意义:只要值不必具有后续值(即您可以手动分配值,而不是依赖于自动编号),您可能有多个具有相同值的值。 / p>
一种常见做法是以下列方式声明enum
:
typedef enum
{
Stopped,
Playing,
Paused,
count
}PossibleValues;
这样,如果count
总是最后定义 - 它会给你枚举元素的计数,假设编号从0开始并且是结果。
答案 1 :(得分:3)
没有,如果你需要,你可能不应该首先使用enum
。
在您的特定情况下,您希望拨打Count
的用例是什么?
答案 2 :(得分:2)
重新提出类似问题(What is the best way for non sequencial integer c++ enums)的答案,因为它与其他几乎没有答案的问题有关。
您可以使用std :: initializer_list来存储枚举的所有值。
namespace PossibleValues
{
enum Type
{
ZERO= 0,
PLUS180= 180,
PLUS90= 90,
MINUS90= -90
};
constexpr auto Values = {ZERO, PLUS180, PLUS90, MINUS90};
size_t Count() { return Values.size(); }
Type Default() { return *begin(Values); }
}
即使它们没有线性值,也可以迭代枚举值。
我认为你可以从一个带有可变宏的宏中生成枚举,初始化列表和函数,尽管在最好的世界里,这种东西应该在标准中。
编辑:当我使用PossibleValues作为枚举或使用了PossibleValues的结构时,我的编译器会抱怨不完整的类型。使用枚举命名空间有点不寻常,但它工作正常。
答案 3 :(得分:1)
可以改进解决方案stackoverflow.com/a/60216003/12894563。我们可以将枚举表达式保存在静态向量中并进行迭代,获得最小/最大等
用法:
#include <type_traits>
#include <algorithm>
#include <vector>
#include <iostream>
#define make_enum(Name, Type, ...) \
struct Name { \
enum : Type { \
__VA_ARGS__ \
}; \
static auto count() { return values.size(); } \
\
static inline const std::vector<Type> values = [] { \
static Type __VA_ARGS__; return std::vector<Type>({__VA_ARGS__}); \
}(); \
static Type min() \
{ \
static const Type result = *std::min_element(values.begin(), values.end()); \
return result; \
} \
static Type max() \
{ \
static const Type result = *std::max_element(values.begin(), values.end()); \
return result; \
} \
}
make_enum(FakeEnum, int, A = 1, B = 0, C = 2, D);
int main(int argc, char *argv[])
{
std::cout << FakeEnum::A << std::endl
<< FakeEnum::min() << std::endl
<< FakeEnum::max() << std::endl
<< FakeEnum::count() << std::endl;
return 0;
}
答案 4 :(得分:0)
PossibleValues的类型必须是枚举吗?如果您只需要一个行为类似枚举的东西,您可以执行以下操作:
#include <iostream>
#include <functional>
#include <set>
template <typename Representation, typename T>
class Iterable_Strong_Enum
{
private:
struct T_Ptr_Less : public std::binary_function<T const *, T const *, bool>
{
bool operator()(T const * x, T const * y) const
{
return x->get_representation() < y->get_representation();
}
};
public:
typedef std::set<T const *, T_Ptr_Less> instances_list;
typedef typename instances_list::const_iterator const_iterator;
Representation const & get_representation() const { return _value; }
static Representation const & min() { return (*_instances.begin())->_value; }
static Representation const & max() { return (*_instances.rbegin())->_value; }
static T const * corresponding_enum(Representation const & value)
{
const_iterator it = std::find_if(_instances.begin(), _instances.end(), [&](T const * e) -> bool
{
return e->get_representation() == value;
});
if (it != _instances.end())
{
return *it;
}
else
{
return nullptr;
}
}
bool operator==(T const & other) const { return _value == other._value; }
bool operator!=(T const & other) const { return _value != other._value; }
bool operator< (T const & other) const { return _value < other._value; }
bool operator<=(T const & other) const { return _value <= other._value; }
bool operator> (T const & other) const { return _value > other._value; }
bool operator>=(T const & other) const { return _value >= other._value; }
static bool is_valid_value(Representation const & value) { return corresponding_enum(value) != nullptr; }
static typename instances_list::size_type size() { return _instances.size(); }
static const_iterator begin() { return _instances.begin(); }
static const_iterator end() { return _instances.end(); }
protected:
explicit Iterable_Strong_Enum(Representation const & value);
private:
Representation _value;
static instances_list _instances;
};
template <typename Representation, typename T>
Iterable_Strong_Enum<Representation, T>::Iterable_Strong_Enum(Representation const & value)
: _value(value)
{
_instances.insert(static_cast<T const *>(this));
}
class PossibleValues : public Iterable_Strong_Enum<int, PossibleValues>
{
public:
static const PossibleValues Stopped;
static const PossibleValues Playing;
static const PossibleValues Pause;
protected:
private:
explicit PossibleValues(int value);
};
PossibleValues::PossibleValues(int value) : Iterable_Strong_Enum<int, PossibleValues>(value) { }
// you need to call that explicitly
Iterable_Strong_Enum<int, PossibleValues>::instances_list Iterable_Strong_Enum<int, PossibleValues>::_instances;
const PossibleValues PossibleValues::Stopped(0);
const PossibleValues PossibleValues::Playing(1);
const PossibleValues PossibleValues::Pause(2);
void stackoverflow()
{
std::cout << "There are " << PossibleValues::size() << " different possible values with representation: " << std::endl;
for (auto pv = PossibleValues::begin(); pv != PossibleValues::end(); ++pv)
{
PossibleValues possible_value = **pv;
std::cout << possible_value.get_representation() << std::endl;
}
}
我对这个解决方案感到有些不满。一方面,它相当普遍,另一方面它是一个小问题的大锤子。