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
Implementation of operators for enum 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 class
types.
继“枚举类”的递增和递减问题的讨论之后,我想询问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;
}
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 class
is 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_t
and 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-n
as 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;
}