True, if you want different enumerations in the same area to have the "last" element, this is not possible. In this case, you will need the prefix "last" value with the name enum, for example (similar to the second listing example.
Regarding the presence of the “last” entry for checking arguments, I may have missed your point. For example, if you pass enum as an argument to a function, for example.
void f( enum fileType type )
It does not provide more "type security" than fileType has without a "last" element.
The only reason I can see the last element (and then for the first element!) Is if you want to iterate over the elements of the enumeration elements in a loop. For example:
enum E { first, e1 = first, e2, e3, last } for (int i = first; i < last; ++i) ...
if so, is it not too artificial to want to have two different enumerations with the same name as the last element? Where is the advantage of having one “last” in two different enumerations?
Does its readability improve compared to, for example, E_last and F_last?
Or does it help keep a record of even one line of code?
Also, what is the “last” in parentheses used for? Copied enumerations are not converted to int by default. (But maybe I misunderstood what you wanted to do ...)
About "you cannot keep the enumeration values short ..." I'm afraid I did not understand what you wanted to say here. Was it about me using "const unsigned short" in the constant group example?
And do not get me wrong, in your case (wanting to process file types) I advocated the use of persistent groups. And with them it is easy and natural to use the same "first" and "last" member (but with different meanings) in different groups. Cm.
namespace nms { typedef const unsigned short G1_t namespace G1 { G1_t first = 1; G1_t type1 = 1; G1_t type2 = 2; G1_t type3 = 3; G1_t last = 3; } typedef const long G2_t namespace G2 { G1_t first = -1; G1_t obj1 = -1; G1_t obj2 = 0; G1_t obj3 = 1; G1_t last = 1; } }
As for "namespace - namespace - enum", I don't like the additional indirectness without significant advantages. As for "namespace - struct / class - enum", this is more philosophical:
C is, so to speak, a sleek and fast bike that made the assembler code written almost unnecessary. It also pretty much belongs to the C lithography operator, which allows you to completely abandon type safety and do what you want without getting the compiler in your path.
C ++ still loves to be a super-set of C. He added classes and object-oriented programming, but still wanted to keep the bike. That was good. Now adding more “safe types” of censuses while maintaining cast operators seems like a desire to add airbags and seat belts to a motorcycle.
I am not against revising transfers to get better transfers (if this is true), but if so, sometimes it is necessary (for example, every 5 years) to discard things that are now recognized as old, obsolete and evil. Otherwise, the language becomes more and more confusing and more difficult for beginners to learn. And, like all programming languages, you need to be easy to use, and this includes not a dozen ways to do the same.