首页 文章

枚举类的运算符的实现

提问于
浏览
29

在讨论了问题Incrementation and decrementation of “enum class”之后,我想问一下 enum class 类型的算术运算符的可能实现 .

原始问题的示例:

enum class Colors { Black, Blue, White, END_OF_LIST };

// Special behavior for ++Colors
Colors& operator++( Colors &c ) {
  c = static_cast<Colors>( static_cast<int>(c) + 1 );
  if ( c == Colors::END_OF_LIST )
    c = Colors::Black;
  return c;
}

有没有办法实现算术运算符而不转换为已定义运算符的类型?我想不出任何东西,但是铸造困扰着我 . 演员通常表示出现了错误,并且必须有充分的理由让他们使用 . 我希望语言允许在不强制使用特定类型的情况下实现运算符 .

Update Dec 2018 :其中一篇针对C17的论文似乎至少部分地通过允许枚举类变量和基础类型之间的转换来解决这个问题:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0138r2.pdf

3 回答

  • 28

    无投射解决方案是使用开关 . 但是,您可以使用模板生成伪开关 . 原理是使用模板列表(或参数包)递归处理枚举的所有值 . 所以,我找到了3种方法 .

    测试枚举:

    enum class Fruit
    {
        apple,
        banana,
        orange,
        pineapple,
        lemon
    };
    

    香草开关(住在这里):

    /// Non-scalable way
    Fruit& operator++(Fruit& f)
    {
        switch(f)
        {
            case Fruit::apple: return f = Fruit::banana;
            case Fruit::banana: return f = Fruit::orange;
            case Fruit::orange: return f = Fruit::pineapple;
            case Fruit::pineapple: return f = Fruit::lemon;
            case Fruit::lemon: return f = Fruit::apple;
        }
    }
    

    C 03-ish方法(住在这里):

    template<typename E, E v>
    struct EnumValue
    {
        static const E value = v;
    };
    
    template<typename h, typename t>
    struct StaticList
    {
        typedef h head;
        typedef t tail;
    };
    
    template<typename list, typename first>
    struct CyclicHead
    {
        typedef typename list::head item;
    };
    
    template<typename first>
    struct CyclicHead<void,first>
    {
        typedef first item;
    };
    
    template<typename E, typename list, typename first = typename list::head>
    struct Advance
    {
        typedef typename list::head lh;
        typedef typename list::tail lt;
        typedef typename CyclicHead<lt, first>::item next;
    
        static void advance(E& value)
        {
            if(value == lh::value)
                value = next::value;
            else
                Advance<E, typename list::tail, first>::advance(value);
        }
    };
    
    template<typename E, typename f>
    struct Advance<E,void,f>
    {
        static void advance(E& value)
        {
        }
    };
    
    /// Scalable way, C++03-ish
    typedef StaticList<EnumValue<Fruit,Fruit::apple>,
            StaticList<EnumValue<Fruit,Fruit::banana>,
            StaticList<EnumValue<Fruit,Fruit::orange>,
            StaticList<EnumValue<Fruit,Fruit::pineapple>,
            StaticList<EnumValue<Fruit,Fruit::lemon>,
            void
    > > > > > Fruit_values;
    
    Fruit& operator++(Fruit& f)
    {
        Advance<Fruit, Fruit_values>::advance(f);
        return f;
    }
    

    C 11-ish方法(住在这里):

    template<typename E, E first, E head>
    void advanceEnum(E& v)
    {
        if(v == head)
            v = first;
    }
    
    template<typename E, E first, E head, E next, E... tail>
    void advanceEnum(E& v)
    {
        if(v == head)
            v = next;
        else
            advanceEnum<E,first,next,tail...>(v);
    }
    
    template<typename E, E first, E... values>
    struct EnumValues
    {
        static void advance(E& v)
        {
            advanceEnum<E, first, first, values...>(v);
        }
    };
    
    /// Scalable way, C++11-ish
    typedef EnumValues<Fruit,
            Fruit::apple,
            Fruit::banana,
            Fruit::orange,
            Fruit::pineapple,
            Fruit::lemon
    > Fruit_values11;
    
    Fruit& operator++(Fruit& f)
    {
        Fruit_values11::advance(f);
        return f;
    }
    

    (C++11-ish old version)

    您可以通过添加一些预处理器来扩展,以消除重复值列表的需要 .

  • 4

    枚举中C中的每个运算符都可以在不转换为基础类型的情况下编写,但结果可能会非常冗长 .

    举个例子:

    size_t index( Colors c ) {
      switch(c) {
        case Colors::Black: return 0;
        case Colors::Blue: return 1;
        case Colors::White: return 2;
      }
    }
    Color indexd_color( size_t n ) {
      switch(n%3) {
        case 0: return Colors::Black;
        case 1: return Colors::Blue;
        case 2: return Colors::White;
      }
    }
    Colors increment( Colors c, size_t n = 1 ) {
      return indexed_color( index(c) + n );
    }
    Colors decrement( Colors c, size_t n = 1 ) {
      return indexed_color( index(c)+3 - (n%3) );
    }
    Colors& operator++( Colors& c ) {
      c = increment(c)
      return c;
    }
    Colors operator++( Colors& c, bool ) {
      Colors retval = c;
      c = increment(c)
      return retval;
    }
    

    并且智能编译器将能够将这些转换为直接在基本积分类型上的操作 .

    但是在 enum class 的界面中转换为基本积分类型并不是一件坏事 . 操作员是 enum class 界面的一部分 .

    如果你不喜欢循环通过 size_t 并认为它是假演员,你可以写:

    Colors increment( Colors c ) {
      switch(c) {
        case Colors::Black: return Colors::Blue;
        case Colors::Blue: return Colors::White;
        case Colors::White: return Colors::Black;
      }
    }
    

    并且类似地用于递减,并且实现递增 n 作为重复 increment 的循环 .

  • -1
    enum class Colors { Black, Blue, White };
    
    Colors operator++(Colors& color)
    {
        color = (color == Colors::White) ? Colors::Black : Colors(int(color) + 1);
        return color;
    }
    

    Check in C++ Shell

相关问题