C++ 2011: Strongly-typed Enums
2011-12-30 10:09
288 查看
Enumerations are an important feature of C++, but unlike in other languages, enumerations are not type-safe.
There are several problems with the enums: they export their enumerators (the members of an enumeration) to the surrounding scope, you cannot specify the underlying type and theenumerators implicitly
convert to int. These problems have been addressed in the new standard with the introduction of "enum class", which are basically strongly-typed enumerations.
Enumerations export their enumerators to the surrounding scope. This has two drawbacks. First, it can lead to name clashes, if two enumerators in different enums declared in the same scope have the same name; second, it's not possible to use an enumerator with
a fully qualified name, including the enum name. Let's take for instance the following enum:
The above line would generate an error with GCC ('Selection' is not a class or namespace) and a warning with VC++ (nonstandard
extension used: enum 'Selection' used in qualified name). To make it work you have to drop the qualification:
The second problem appears if we define a new enumeration in the same scope, having an enumerator with the same name as one from Selection.
Both enums have an enumerator called None; since they are exported in the surrounding scope, there is a name clash. To fix it we can either change the name of an enumerator, or put the enums in different namespaces.
In this case we can write:
Of course, one can argue that using the same name for the namespace and the enum is not a good practice, but this is just an exercise, so you are free to name them however you want.
It is not possible to specify the underlying type. It is implementation specific, but it has to be an integral type; it should not be larger than int unless the enumerator value cannot fit an int or unsigned int. Let's take for example the following definition:
The output is different with different compilers. Compiled with VC++ the program outputs -65536, while compiled with GCC the output is 4294901760. The difference is that VC++ always uses int for the underlying type, while GCC is more adaptive.
Now, some compilers do allow you to specify the underlying type. For instance VC++ allows this since version 2005. MSDN
says the definition of an enum has the form:
And you can actually say:
In which case, printing Multiple will also show 4294901760.
GCC also allows you to specify an underlying type, however, this is actually supported in the standard only in the new C++ 2011 version.
The underlying type leads to another problem: forward declaration is not possible.
The reason is that given only the forward declaration, the compiler does not know the underlying size and thus the size of the enumerator. As a result it cannot compile this piece of code. However, if you try to build this in VC++ you'll notice that it actually
compiles. The reason is VC++ always uses int (unless specified otherwise) for the underlying type (as indicated earlier) so it does know the size of the enumerator.
The values of enumerators implicitly convert to int.
The problem arises when you try to do something like this:
It doesn't matter what function process does; the point is it was supposed to process an int (maybe any value), not Selections. But it slipped into the code and the compiler shows no warnings and the program runs. One could probably figure other cases when
the implicit conversion to int is not desired.
The new standard brings a new kind of enums (the existing ones are left mostly untouched - see below), introduced with 'enum class' and called strongly-typed enums. They no longer export their enumerators to the surrounding scope, can have user specified underlying
type of an integral type (also added for tranditional enums) and do not convert implicitly to int.
The Selection enumeration shown earlier would be defined as:
To use it, one must use the Selection:: qualifier, because the enumerators are no longer exported to the surrounding scope (in this case the global namespace).
And if we add the Border enumeration, the problem with the redefinition of None would no longer happen, since the two None names are members of different types (in different scopes).
By default, the underlying type is int and the same rules as for traditional enums apply if the user does not specify anything else. However, it is possible to do that now, but you can only use an integral type for that.
Since the underlying type can be specified, it will be possible to do forward declaration for enums (for the reasons explained in an earlier paragraph).
However, it is possible to specify the underlying type for traditional enums too. This is also legal in C++ 2011 (and actually supported for some years by various compilers):
Forward declaration with traditional enums will also be possible.
Enumerators of strongly-typed enums no longer implicitly convert to int. The following piece of code will trigger an error:
Instead, you have to write:
This would allow the compiler to immediately flag cases when the enumerators are used in place of an int (as shown earlier), which enable us to write better code.
The new C++ standard brings a new type of enums, referred to as strongly-typed enums and introduced with the 'enum class' keywords. They solve the known problems with the traditional enums: the scope of the enumerators, the possibility to specify the underlying
type (also added for the traditional enums) and the implicit conversion to int which is not supported.
Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming.
He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.
自:http://www.codeguru.com/cpp/cpp/article.php/c19083/C-2011-Strongly-typed-Enums.htm
There are several problems with the enums: they export their enumerators (the members of an enumeration) to the surrounding scope, you cannot specify the underlying type and theenumerators implicitly
convert to int. These problems have been addressed in the new standard with the introduction of "enum class", which are basically strongly-typed enumerations.
Problems with traditional enums
The Scope
Enumerations export their enumerators to the surrounding scope. This has two drawbacks. First, it can lead to name clashes, if two enumerators in different enums declared in the same scope have the same name; second, it's not possible to use an enumerator witha fully qualified name, including the enum name. Let's take for instance the following enum:
enum Selection { None, Single, Multiple }; Selection sel = Selection::Single;
The above line would generate an error with GCC ('Selection' is not a class or namespace) and a warning with VC++ (nonstandard
extension used: enum 'Selection' used in qualified name). To make it work you have to drop the qualification:
Selection sel = Single;
The second problem appears if we define a new enumeration in the same scope, having an enumerator with the same name as one from Selection.
enum Border{ None, Flat, Raised, Sunken};
Both enums have an enumerator called None; since they are exported in the surrounding scope, there is a name clash. To fix it we can either change the name of an enumerator, or put the enums in different namespaces.
namespace Selection { enum Selection { None, Single, Multiple }; } namespace Border { enum Border { None, Flat, Raised, Sunken }; }
In this case we can write:
Selection::Selection sel = Selection::Single;
Of course, one can argue that using the same name for the namespace and the enum is not a good practice, but this is just an exercise, so you are free to name them however you want.
The Underlying Type
It is not possible to specify the underlying type. It is implementation specific, but it has to be an integral type; it should not be larger than int unless the enumerator value cannot fit an int or unsigned int. Let's take for example the following definition:namespace Selection { enum Selection { None = 0, Single = 1, Multiple = 0xFFFF0000U }; } cout << Selection::Multiple << endl;
The output is different with different compilers. Compiled with VC++ the program outputs -65536, while compiled with GCC the output is 4294901760. The difference is that VC++ always uses int for the underlying type, while GCC is more adaptive.
Now, some compilers do allow you to specify the underlying type. For instance VC++ allows this since version 2005. MSDN
says the definition of an enum has the form:
enum [tag] [: type] {enum-list} [declarator];
And you can actually say:
namespace Selection { enum Selection : unsigned int { None = 0, Single = 1, Multiple = 0xFFFF0000U }; }
In which case, printing Multiple will also show 4294901760.
GCC also allows you to specify an underlying type, however, this is actually supported in the standard only in the new C++ 2011 version.
The underlying type leads to another problem: forward declaration is not possible.
enum Selection; void make_selection(Selection s) { } enum Selection { None, Single, Multiple };
The reason is that given only the forward declaration, the compiler does not know the underlying size and thus the size of the enumerator. As a result it cannot compile this piece of code. However, if you try to build this in VC++ you'll notice that it actually
compiles. The reason is VC++ always uses int (unless specified otherwise) for the underlying type (as indicated earlier) so it does know the size of the enumerator.
Conversion to int
The values of enumerators implicitly convert to int.int s1 = None;
The problem arises when you try to do something like this:
enum Selection { None = 0, Single = 1, Multiple = 2, }; void process(int value) { /* do something */ } process(Multiple);
It doesn't matter what function process does; the point is it was supposed to process an int (maybe any value), not Selections. But it slipped into the code and the compiler shows no warnings and the program runs. One could probably figure other cases when
the implicit conversion to int is not desired.
Strongly-typed Enums in C++ 2011
The new standard brings a new kind of enums (the existing ones are left mostly untouched - see below), introduced with 'enum class' and called strongly-typed enums. They no longer export their enumerators to the surrounding scope, can have user specified underlyingtype of an integral type (also added for tranditional enums) and do not convert implicitly to int.
The scope
The Selection enumeration shown earlier would be defined as:enum class Selection { None, Single, Multiple, };
To use it, one must use the Selection:: qualifier, because the enumerators are no longer exported to the surrounding scope (in this case the global namespace).
Selection s = Selection::Multiple;
And if we add the Border enumeration, the problem with the redefinition of None would no longer happen, since the two None names are members of different types (in different scopes).
enum class Border { None, Flat, Raised, Sunken };
The Underlying Type
By default, the underlying type is int and the same rules as for traditional enums apply if the user does not specify anything else. However, it is possible to do that now, but you can only use an integral type for that.enum class Selection : unsigned char { None, Single, Multiple, };
Since the underlying type can be specified, it will be possible to do forward declaration for enums (for the reasons explained in an earlier paragraph).
enum class Selection : unsigned char;
void make_selection(Selection s)
{
}
enum class Selection : unsigned char { None, Single, Multiple, };
However, it is possible to specify the underlying type for traditional enums too. This is also legal in C++ 2011 (and actually supported for some years by various compilers):
enum Selection : unsigned char { None, Single, Multiple, };
Forward declaration with traditional enums will also be possible.
Implicit Conversion
Enumerators of strongly-typed enums no longer implicitly convert to int. The following piece of code will trigger an error:int s = Selection::Multiple;
Instead, you have to write:
Selection s = Selection::Multiple;
This would allow the compiler to immediately flag cases when the enumerators are used in place of an int (as shown earlier), which enable us to write better code.
Conclusions
The new C++ standard brings a new type of enums, referred to as strongly-typed enums and introduced with the 'enum class' keywords. They solve the known problems with the traditional enums: the scope of the enumerators, the possibility to specify the underlyingtype (also added for the traditional enums) and the implicit conversion to int which is not supported.
About the Author
Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming.He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.
自:http://www.codeguru.com/cpp/cpp/article.php/c19083/C-2011-Strongly-typed-Enums.htm
相关文章推荐
- 2011阿里巴巴集团实习生招聘笔试题 C&C++ .
- C++面试宝典2011
- 读书时间 05/25/2011 Linux C/C++ 孤儿进程
- C++中使用array报错 requires compiler and library surpport for the ISO c++ 2011 standard
- 如何在Kdevelop中加入C++2011的支持
- [C++ 2011 STL (VS2012 Update4) 源代码阅读系列(1)]准备知识
- C++2011程序中的未定义行为
- [C++ 2011 STL (VS2012 Update4) 源代码阅读系列(3)]模板函数指针的的各种组合以及调用的方式
- hdu 3664(第二欧拉数,HIT2011春省赛选拔赛通过,hdu G++ 46MS, C++ 31MS通过)
- (转载)C++面试宝典2011版
- 2011阿里巴巴集团实习生招聘笔试题 C&C++
- CSDN论坛 c/c++大版 2011-10 帖子整理(整理中)
- 杭电OJ2011 C++
- error This file requires compiler and library support for the ISO C++ 2011 standard
- C++程序员2011总结
- 2011阿里巴巴集团实习生招聘笔试题 C&C++
- C++面试宝典2011版
- C++ 错误error C2011:类型重定义的解决办法
- C++实现FTP参考代码 2011
- C++面试宝典2011版 .