The text previously defined Scope as "the location in a program where a specific name is visible and accessible." Scope is a property of any programming element a programmer names: variables, functions, structures, classes, enumerations, etc. The name becomes a symbol in the program. The compiler component saves the symbol in its symbol table, mapping the symbol name to the corresponding element's address in the case of a definition. Programmers typically specify basic enumerations in global scope, giving their symbolic constants global scope. However, global scope often causes problems.
enum hurricane { GAMMA, LAURA, MARCO }; enum radiation { ALPHA, BETA, GAMMA }; enum function { FACTORIAL, GAMMA };
redeclaration of 'GAMMA'
and aborts the compilation.
If the enumerations are part of only one program, programmers can easily rename the enumeration elements: radiation_ALPHA and function_GAMMA. Aside from being inelegant, this approach fails when a program utilizes the client-supplier organization, bringing both suppliers together in a single application client. If each enumeration is part of an otherwise independent supplier, programmers may not have the supplier's source code, making it impossible to rename the enumeration elements.
We first encountered the scope resolution operator during the introduction of C++'s I/O system. Two adjacent semicolons, without any intervening space, form it. While it can operate either as a unary or binary operator, the latter is the most frequently used version. The binary version is left associative, processing its operands from left to right.
Programmers narrow a feature's scope by enclosing it in a container: a structure, namespace, or class. In this sense, enumerations are containers holding a set of symbolic constants. C++ also allows the nesting of one container in another. In conjunction with a container, the operator permits programmers to manage the scope of a programming element. The left-hand operand names a scoping container, while the right-hand operand names the final feature or a nested container. In the following example, programmers use it to select elements specified in a specific enumeration or structure.
The conflicting enumerations, radiation and function, each have a well-defined meaning within a given context:
These observations suggest that including GAMMA in multiple enumerations and using them in the same program should be valid. But specifying the various GAMMA elements in the same (global) scope causes the conflict. We can limit their scope, thereby eliminating the conflict, by embedding the enumerations inside a structure and extending the access syntax. (The following examples demonstrate syntax but don't otherwise solve a "real" problem.)
#include <iostream> #include <string> using namespace std; enum hurricane { GAMMA, LAURA, MARCO }; struct Decay { enum radiation { ALPHA, BETA, GAMMA }; string isotope; radiation mode; double halflife; }; struct Math { enum function { FACTORIAL, GAMMA }; }; |
int main() { int h1 = GAMMA; int h2 = ::GAMMA; Decay pu = { "Pu-239", Decay::radiation::GAMMA, 2.41e4 }; int i2 = pu.mode; cout << pu.mode << endl; int i1 = Decay::radiation::GAMMA; int m = Math::GAMMA; return 0; } |
(a) | (b) |
Decay::radiation::GAMMA
as follows:
Scoped enumerations are similar in appearance and simplicity to the initial, conflicting example - nevertheless, a simple syntax update made in 2011 resolves the conflict. Adding one of two existing keywords to the enumeration specification creates a new, named scope and binds the enumeration specification to it. Programmers can choose either enum class
or enum struct
; both versions have the same effect, but the first is preferred.
#include <iostream> using namespace std; enum Hurricane { GAMMA, LAURA, MARCO }; enum class radiation { ALPHA, BETA, GAMMA }; enum class function { FACTORIAL, GAMMA }; int main() { int h1 = GAMMA; int h2 = ::GAMMA; function f = function::GAMMA; radiation test = radiation::GAMMA; switch (test) { case radiation::ALPHA : cout << "radiation Alpha\n"; break; case radiation::BETA : cout << "radiation Beta\n"; break; case radiation::GAMMA : cout << "radiation Gamma\n"; break; } return 0; }