【问题标题】:Changing enum to next value [C++11]将枚举更改为下一个值 [C++11]
【发布时间】:2016-12-05 17:03:42
【问题描述】:

我想做的是使用枚举轻松指定不同的绘制模式。到目前为止,这就是我所拥有的:

class Grid {
   enum drawMode { GRID, EROSION, RIVERS, HUMIDITY, ATMOSPHERE }
   drawMode activeDraw;

   void draw() {
      switch(activeDraw) {
      case GRID:
         drawGrid();
         break;
      case EROSION:
         drawErosion();
         break;
      // etc..
   }

   void keyPressed(int key) {
      switch(key) {
      case ' ':
         // Cycle activeDraw to next drawMode
      }
}

因此,如果用户点击空格键,activeDraw 将更改为枚举中的下一个值。因此,如果当前的 activeDraw 在击中空间后是 GRID,它将更改为 EROSION,如果 activeDraw 是 ATMOSPHERE,它将更改为 GRID。
有一个简单的解决方案吗? 谢谢。

【问题讨论】:

  • 这个问题与 C++11 有什么关系?我建议使用enum class 可以将这个问题限定为 C++11。
  • 这是相关的,因为在 C++11 中,您必须手动转换它才能将其作为整数使用,这是重点。

标签: c++ c++11 enums switch-statement


【解决方案1】:

正如 Maroš Beťko 所说,要将 1 加到变量中,您必须将值转换为 int 并返回:

activeDraw = static_cast<drawMode>(static_cast<int>(activeDraw) + 1);

如果枚举是在没有 C++11 enum class 语法的情况下定义的(如问题文本中所示),则无需转换为 int

activeDraw = static_cast<drawMode>(activeDraw + 1);

要使其循环回零,请使用整数算术,模运算符:

activeDraw = static_cast<drawMode>((activeDraw + 1) % (ATMOSPHERE + 1));    

要消除一个丑陋的+1,在枚举中添加另一个元素:

enum drawMode { ..., ATMOSPHERE, NUM_DRAW_MODES };
...
activeDraw = static_cast<drawMode>((activeDraw + 1) % NUM_DRAW_MODES);

如果您经常使用,也可以将此代码填充到operator++

drawMode operator++(drawMode& mode)
{
    mode = static_cast<drawMode>((mode + 1) % NUM_DRAW_MODES);
    return mode;
}

drawMode operator++(drawMode& mode, int) // postfix operator
{
    drawMode result = mode;
    ++mode;
    return result;
}

enums 的重载运算符很少使用,有些人认为它过大(不好),但它会使您的代码更短(并且可以说更简洁)。

【讨论】:

  • 请注意,模运算符非常慢,在这种情况下过度杀伤力。您只需要检查新值是否为NUM_DRAW_MODES,如果是,则将其归零。如果您实际上可能最终超出列表末尾不止一步,则最好使用模数。
  • 编译器可能会优化模运算。
【解决方案2】:

由于您的枚举没有强制值,您可以“增加”它们,并在需要时对最后一项 + 1 进行取模以重置为第一项:

 activeDraw = drawMode((activeDraw+1) % (ATMOSPHERE+1));

顺便说一句:稍加修改后也可以在 C 语言中使用:

activeDraw = (activeDraw+1) % (ATMOSPHERE+1);

【讨论】:

    【解决方案3】:

    这是你应该写一次,使用很多地方的东西。

    boost 有一些可能有用的运算符库。如果你需要自己写,这里有一个例子:

    namespace EnumOps {
      // ADL helper.  See #define below for macro that writes
      // the "this enum should use enum ops" overload:
      template<class T>
      std::false_type use_enum_ops_f(T&&){return {};}
    
      // trait class that detects if we should be messing with this enum:
      template<class T>
      using use_enum_ops = decltype(use_enum_ops_f( std::declval<T>() ));
    
      // to-from underlying type:
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      constexpr std::underlying_type_t<E> get_underlying(E e) {
        return static_cast<std::underlying_type_t<E>>(e);
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      constexpr E from_underlying(std::underlying_type_t<E> e) {
        return static_cast<E>(e);
      }
    
      // Clamps your Enum value from 0 to E::MAX_VALUE using modular arithmetic
      // You must include a MAX_VALUE in your enum.
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E clamp_max( std::underlying_type_t<E> e ) {
        constexpr auto max = get_underlying(E::MAX_VALUE);
        if (e < 0) {
          auto count = -(e-max+1)/max;
          e =  e + count*max;
        }
        return from_underlying<E>(e % max);
      }
    
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E& operator+=( E& e, std::underlying_type_t<E> x ) {
        e= clamp_max<E>(get_underlying(e) + x);
        return e;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E& operator-=( E& e, std::underlying_type_t<E> x ) {
        e= clamp_max<E>(get_underlying(e) - x);
        return e;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E operator+( E e, std::underlying_type_t<E> x ) {
        return e+=x;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E operator+( std::underlying_type_t<E> x, E e ) {
        return e+=x;
      }
      // no int - enum permitted, but enum-int is:
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E operator-( E e, std::underlying_type_t<E> x ) {
        e -= x;
        return e;
      }
      // enum-enum returns the distance between them:
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      std::underlying_type_t<E> operator-( E lhs, E rhs ) {
        return get_underlying(lhs) - get_underlying(rhs);
      }
      // ++ and -- support:
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E& operator++( E& lhs ) {
        lhs += 1;
        return lhs;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E operator++( E& lhs, int ) {
        auto tmp = lhs;
        ++lhs;
        return tmp;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E& operator--( E& lhs ) {
        lhs -= 1;
        return lhs;
      }
      template<class E,
        std::enable_if_t< use_enum_ops<E>{}, int> =0
      >
      E operator--( E& lhs, int ) {
        auto tmp = lhs;
        --lhs;
        return tmp;
      }
    }
    // use this macro in the namespace of your enum
    // passing it your enun name:
    #define ENABLE_ENUM_OPS(...) \
      std::true_type use_enum_ops_f(__VA_ARGS__){return {};}
    // Where you wnat to use ops, you must also
    // using namespace EnumOps;
    

    使用示例:

    namespace somewhere {
      enum class bob { A, B, C, MAX_VALUE };
      ENABLE_ENUM_OPS(bob)
    }
    
    int main() {
      using namespace EnumOps;
      auto x = somewhere::bob::A;
      ++x;
      std::cout << (x == somewhere::bob::B) << "\n";
      x+=3;
      std::cout << (x == somewhere::bob::B) << "\n";
      x-=4;
      std::cout << (x == somewhere::bob::A) << "\n";
    }
    

    live example.

    这使用了适量的 C++14——std::underlying_type_t&lt;E&gt;。替换为typename std::underlying_type&lt;E&gt;::type。对于我潜入的任何其他 _t 别名也类似。

    它使用了 MSVC 2015 惨遭失败的 C++11 功能。使用 C++11 编译器来解决这个问题。它可能最初在 MSVC 2015 中工作,但不要被愚弄。我没有在 MSVC 2017 上尝试过。

    【讨论】:

      【解决方案4】:

      枚举本质上只是命名的整数,因此您可以这样对待它们。

      如果您在枚举末尾添加 NUM_DRAW_MODES 值以跟踪计数,那么这应该可以工作:

      void keyPressed(int key) {
        switch(key) {
        case ' ':
          // Cycle activeDraw to next drawMode
          ++activeDraw;
          if (activeDraw >= NUM_DRAW_MODES) {
            activeDraw = GRID;
          }
        }
      

      如果您使用 C++11 的 enum class,那么您将不得不 static_cast 来回转换 int 值,而不是依赖隐式转换。

      【讨论】:

      • 我无法在 C++ 中编译 ++activeDraw:` 错误:不匹配 'operator++'(操作数类型是 'drawMode')`,无论是在 C++11 还是在 03 中。
      • 是的,Jean 也是我的问题。我缺少的是静态转换为 int 并返回,以便 + 运算符像这样工作:activeDraw = static_cast(static_cast(activeDraw) + 1);
      • 这应该实现为重载的operator++(drawMode)operator++(drawMode, int)
      猜你喜欢
      • 2018-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多