C++ 枚举类运算符的实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15451382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:23:05  来源:igfitidea点击:

Implementation of operators for enum class

c++c++11operator-overloadingenum-class

提问by SomeWittyUsername

Following the discussion in question Incrementation and decrementation of “enum class”, I'd like to ask about the possible implementation of arithmetic operators for enum classtypes.

“枚举类”的递增和递减问题的讨论之后,我想询问enum class类型算术运算符的可能实现。

Example from the original question:

来自原始问题的示例:

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

Is there a way to implement arithmetic operators without casting to a type with already defined operators? I can't think of any, but casting bothers me. Casts are usually indication of something wrong and there has to be a very good reason for their usage. I would expect the language to allow implementation of an operator to be achievable without forcing to a specific type.

有没有一种方法可以实现算术运算符而无需强制转换为具有已定义运算符的类型?我什么也想不出来,但演员让我很困扰。演员表通常表明有问题,并且必须有很好的理由使用它们。我希望该语言能够在不强制使用特定类型的情况下实现运算符的实现。

Update Dec 2018: One of the papers towards C++17 seems to address this at least partially by allowing conversions between enum class variable and the underlying type: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0138r2.pdf

2018 年 12 月更新:其中一篇关于 C++17 的论文似乎通过允许枚举类变量和底层类型之间的转换至少部分解决了这个问题:http: //www.open-std.org/jtc1/sc22/wg21/ docs/papers/2016/p0138r2.pdf

采纳答案by Synxis

The no-cast solution is to use switch. However, you can generate a pseudo-switch using templates. The principle is to recursively process all values of the enum using a template list (or a parameter pack). So, here are 3 methods I found.

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

Test enum:

测试枚举:

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

The vanilla switch (live here):

香草开关(住在这里)

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

The C++03-ish method (live here):

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

The C++11-ish method (live here):

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)

(C++11-ish 旧版本)

You may be able to extend by adding some preprocessor to remove the need to repeat the list of values.

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

回答by Yakk - Adam Nevraumont

Every operator in C++ on enums can be written without casting to an underlying type, but the result would be ridiculously verbose.

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

As an example:

举个例子:

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

and a smart compiler will be able to turn these into operations that are directly on the base integral type.

智能编译器将能够将这些转换为直接基于基本整数类型的操作。

But casting to a base integral type in the interface of your enum classis not a bad thing. And operators are part of the interface for your enum class.

但是在你的接口中enum class转换为基本的整数类型并不是一件坏事。操作符是您的enum class.

If you don't like that loop through size_tand consider it a fake cast, you can just write:

如果你不喜欢那个循环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;
  }
}

and similarly for decrement, and implement increment-by-nas loops of repeated increment.

和类似地为 decrement,并实现 increment-by-n作为重复的循环increment

回答by vigord

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

签入 C++ Shell