5

Why isn't this valid C++?:

enum foo : unsigned { first_foo, second_foo };
enum bar : foo { best_foo = first_foo };

GCC 5.4.0 says:

/tmp/a.cpp:3:16: error: underlying type ‘foo’ of ‘bar’ must be an integral type
     enum bar : foo { best_foo = first_foo };

I can understand why I would get this error if foo were a float, or some struct, or what-not. But this seems perfectly legit to me in terms of semantics, type safety etc. What am I missing?

8
  • 2
    Why? Because standard says so. :) Commented Nov 15, 2016 at 10:18
  • 1
    @Zereges: But... why? Commented Nov 15, 2016 at 10:19
  • 1
    The usual reason is that some people wrote a proposal and what they proposed was accepted. Commented Nov 15, 2016 at 10:43
  • For me it is a logical error to say a enum can be based on an another enum. If you think that the base is the storage type where the values defined in the enum are stored in, I can not get it as a logic expression to say the storage type is an enum. Because the enum is not only the storage type, it also contains a set of valid values. So what should be an enum based on an other enum be?Only a subset of the underlaying enum, because all other values are not defined in the underlaying one? Why should a enum not be based on a bitfield which also represents some kind of integral storage type? Commented Nov 15, 2016 at 10:57
  • @Klaus: See my motivation here. But you make a valid point, perhaps you should make that an answer. Commented Nov 15, 2016 at 10:59

4 Answers 4

12

C++11 [dcl.enum]/2:

The type-specifier-seq of an enum-base shall name an integral type; any cv-qualification is ignored.

Enums are not themselves integral types – [basic.fundamental]/7:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.

This is accompanied by a non-normative footnote:

Therefore, enumerations are not integral; however, enumerations can be promoted to integral types as specified in [conv.prom].

To achieve the effect I think you're looking for, however, is still simple:

enum bar : std::underlying_type<foo>::type { best_foo = first_foo };
Sign up to request clarification or add additional context in comments.

7 Comments

Ok, that's the immediate formal reason - but why must type-specifier-seq name an integral type rather than either an integral type or an enum type?
@einpoklum : That's a far-reaching question for this sort of site. I'm not on the C++ committee, so I'm not sure how I could know. ;-]
Well, sometimes people who are either on the committee or following its work frequent this site and answer questions. Bjarne even answered me once.
@einpoklum I suspect the answer is that they didn't deem it important. If you want it to be changed, write a proposal :).
Inspired by this answer, I check the syntax of integral types. en.cppreference.com/w/cpp/language/type [ integral types (see also std::is_integral)]
|
5

When you add things to C++, you tend to add the minimium amount that solves a problem.

The enum A:int syntax lets you specify exactly how the enum A is stored as an integer. That is all it does, and it solves the problem.

enum A:B where B is an enum could have many meanings. A could extend B, or A could be a subset of the B underlying type with different names, or A could be an enum strictly restricted to have a subset of the values that can be stored within B, or A could be an enum whose values are restricted to the "hull" of B values.

Of these, the "same underlying type" solution (with the :int syntax) lines up with enum A:std::underlying_type_t<B>. So there is already a way to do that, and it isn't particularly burdensome.

Until someone makes a suggestion for what enum A:B should mean and convinces enough of the committee, it is unlikely to be permitted. And when there are multiple different reasonable meanings, this makes it harder for any one meaning to be chosen. One would need a strong use case why one particular meaning was best, and why it is worth the effort to put it in the standard.

1 Comment

Well, then, if you like - you could have a look at my use case and chime in on whether/to what extent you find it compelling.
3

Well, not exactly the same, but I think you could use std::underlying_type:

enum foo : unsigned { first_foo, second_foo };
enum bar : std::underlying_type_t<foo> { best_foo = first_foo };

1 Comment

@einpoklum Well, you didn't mention that in your question, so how could I possibly know? I don't know why it doesn't satisfy your needs, either; the regular enum is pretty weak when it comes to underlying type conversions anyway.
3

For me it is a logical error to say a enum can be based on an another enum. If you think that the base is the storage type where the values defined in the enum are stored in, I can not get it as a logic expression to say the storage type is an enum. Because the enum is not only the storage type, it also contains a set of valid values.

If we can write something like:

enum A: int { ONE, TWO };

what should mean:

enum B: A{};

Because A defines not only the underlaying type ( I call this the storage type ) but also a set of valid values, should B only a subset of the enum A which means that you can define only values already defined in A?

Is it now valid to say we habe the values ONE,TWO already defined also in B? And is it now possible to add more values like:

    enum B: A{THREE};

and all valid values are now ONE,TWO,THREE ?

or is the meaning we get only the subset:

    enum B: A{ONE};

which means B can only use values already defined in A. Simply that makes it difficult to me to make a enum a base of another enum.

If you open that door, you also can also come to the idea that you want to use the underlaying storage type from other kinds of types like bitfields.

struct A { unsigned int a : 3; unsigned int B: 2; };

should then

enum B: A { ONE, TWO };

also be valid? I believe not! ;)

From the logical point ( my point of view ) the underlaying type of the enum is its storage type. And it makes no sense to make an enum as an underlaying storage type for another enum at all.

3 Comments

"should Be only be a subset of the enum A"? Yes, I would think so actually.
@einpoklum If it can only be a subset, it is the opposite of deriving from a struct/class. Because you can only extend a base class in the derived one. So is it logical ok for you to reverse the logic for enums? ;)
Klaus: That's not true. Inheritance is often used to constrict the base class rather than expand it. You have both "Is-a-kind-of which-also" and "is-a-kind-of which-only" relations between inherited classes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.