3

我在用 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);

笔记:

  1. 关键字constexpr(我注释/*expr*/了,将其保留为const)并且noexcept仅因为我使用的编译器(Visual C++ 2013)在当前版本不支持它们而被注释。
  2. 也许您不需要逻辑来临时取消定义 min 和 max 宏。
  3. 我知道这default()不适合“数字限制”范围;但它似乎是一个方便的地方。default甚至它与在某些情况下是关键字的词重合!
4

5 回答 5

3

不,没有,如果你需要这个,你可能一开始就不应该使用 an enum

在您的特定情况下,您想要调用的用例是什么Count

于 2012-10-19T10:29:47.573 回答
3

AFAIK 没有自动编译器支持的关键字来获取enum. OTOH这通常没有意义:您可能有多个具有相同值的值,只要这些值不必具有后续值(即您可以手动分配值,而不是依赖自动编号)。

一种常见的做法是enum通过以下方式声明:

  typedef enum
    {
        Stopped, 
        Playing, 
        Paused,

        count

    }PossibleValues;  

这样,如果count总是最后定义 - 它会给你枚举元素的计数,假设编号从 0 开始并且是随后的。

于 2012-10-19T10:30:57.353 回答
3

重新发布类似问题的答案(非序列整数 c++ 枚举的最佳方法是什么),因为它与一个几乎没有答案的问题有点相关。

您可以用来获得所需内容的模式是使用 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 使用结构时,我的编译器会抱怨类型不完整。为枚举使用命名空间有点不寻常,但它工作正常。

于 2015-05-27T13:16:06.020 回答
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;
}
于 2020-02-17T22:45:24.460 回答
0

可能值的类型必须是枚举吗?如果您只需要一些行为类似于枚举的东西,您可以执行以下操作:

#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;
  }
}

我对那个解决方案有点失望。一方面,它非常笼统,另一方面,它是解决小问题的大锤。

于 2012-10-19T11:38:37.900 回答