Why cast between two strongly typed C++11 enum classes is allowed?

7,305 views
Skip to first unread message

Vicente J. Botet Escriba

unread,
Jan 9, 2017, 11:45:33 AM1/9/17
to std-dis...@isocpp.org

Hi,

C++11 strong enums ensure that we can not interchange 2 such enum classes as we can not interchange two independent structs.

However we can static_cast two enum classes

        EC2 ec = static_cast<EC2>(e);

        EC2 ec = EC2(e);

while we can not static cast two independent structs

        S2 ec = static_cast<S2>(s2);

See http://melpon.org/wandbox/permlink/ne5oPVPMkkrswIxy


I'm asking this because the Boost.ScopedEnum emulation for C++98 of a C++11 enum class uses a struct to represent the scope and so the cast between two emulated scoped enums is not supported in C++98. I find that this is a good thing.

I'm wondering if the support of the cast between two C++11 enum classes was intentional or not.

Do you find it a desirable feature?

Note that if the static_cast between two enum classes was not supported we could always do it using two static casts

        EC2 ec = static_cast<EC2>(static_cast<int>(e));


Vicente


Nicol Bolas

unread,
Jan 9, 2017, 12:31:30 PM1/9/17
to ISO C++ Standard - Discussion


On Monday, January 9, 2017 at 11:45:33 AM UTC-5, Vicente J. Botet Escriba wrote:

Hi,

C++11 strong enums ensure that we can not interchange 2 such enum classes as we can not interchange two independent structs.


No, it does not. It ensures that we cannot implicitly convert integers to strong enums or strong enums to integers. That is the purpose of the feature: to prevent inappropriate implicit conversions.

Strongly typed `enum`s in C++ are still integers, even if they're not implicitly convertible from/to integers. Explicit conversions are therefore able to convert them from/to integer types. And since enums are integers, you can explicitly convert them from/to enum types.

Vicente J. Botet Escriba

unread,
Jan 9, 2017, 5:45:13 PM1/9/17
to std-dis...@isocpp.org
Le 09/01/2017 à 18:31, Nicol Bolas a écrit :


On Monday, January 9, 2017 at 11:45:33 AM UTC-5, Vicente J. Botet Escriba wrote:

Hi,

C++11 strong enums ensure that we can not interchange 2 such enum classes as we can not interchange two independent structs.



No, it does not.
I meant implicitly here of course. the next paragraph continue with explicit conversions. Sorry for the imprecision.

It ensures that we cannot implicitly convert integers to strong enums or strong enums to integers. That is the purpose of the feature: to prevent inappropriate implicit conversions.

Strongly typed `enum`s in C++ are still integers, even if they're not implicitly convertible from/to integers. Explicit conversions are therefore able to convert them from/to integer types. And since enums are integers, you can explicitly convert them from/to enum types.

My question is if you believe this behavior is desirable. I don't. Even if enums are integer types they are strong types.
I would expect them to be able to accept an explicit conversion to the underlying type only and not other integer types, and in particular not to other strong enums (enum classes). If I want to have this behavior I would need to use a struct similar to the one in Boost.ScopedEnums, wrapping the underlying type :(

I know it is too late to change it. I just want to know if this was intentional (considered during the design) or if it is there by omission.

Vicente

Nicol Bolas

unread,
Jan 9, 2017, 6:17:38 PM1/9/17
to ISO C++ Standard - Discussion
On Monday, January 9, 2017 at 5:45:13 PM UTC-5, Vicente J. Botet Escriba wrote:
Le 09/01/2017 à 18:31, Nicol Bolas a écrit :


On Monday, January 9, 2017 at 11:45:33 AM UTC-5, Vicente J. Botet Escriba wrote:

Hi,

C++11 strong enums ensure that we can not interchange 2 such enum classes as we can not interchange two independent structs.



No, it does not.
I meant implicitly here of course. the next paragraph continue with explicit conversions. Sorry for the imprecision.
It ensures that we cannot implicitly convert integers to strong enums or strong enums to integers. That is the purpose of the feature: to prevent inappropriate implicit conversions.

Strongly typed `enum`s in C++ are still integers, even if they're not implicitly convertible from/to integers. Explicit conversions are therefore able to convert them from/to integer types. And since enums are integers, you can explicitly convert them from/to enum types.

My question is if you believe this behavior is desirable.

I believe that if there is going to be a case where `static_cast<Y>(static_cast<X>(something))` is legal, while `static_cast<Y>(something)` is not, then there needs to be a really good reason for it. Like a loss of information. Or something that is almost certain to provoke undefined behavior (`static_cast<vector<int>*>(static_cast<void*>(not_a_vector_int))`).

So long as the sequence of steps to convert one enum type to another is both legal and perfect, I see no reason to make that conversion take longer to spell.

I don't. Even if enums are integer types they are strong types.

Many people have different ideas about what "strong type" means with regard to this. It's one of the things that makes strong aliases difficult to design.

I would expect them to be able to accept an explicit conversion to the underlying type only and not other integer types, and in particular not to other strong enums (enum classes). If I want to have this behavior I would need to use a struct similar to the one in Boost.ScopedEnums, wrapping the underlying type :(

Well... how many people want that behavior? Or more importantly, how many people need that behavior? You're talking about forbidding people from doing something that they're not really doing very much as is.

Strong enums exist to prevent accidents. Mis-using `static_cast` is not an accident; it is perfidy. Whether that's a `static_cast` from a raw integer or from an enum, it's still easy to find and deal with.
 
I know it is too late to change it. I just want to know if this was intentional (considered during the design) or if it is there by omission.

N1579(PDF) was the first paper I could find on strongly-typed enums. In that proposal, a strong enum was still implicitly convertible to an integer by default; the proposal was originally mostly about scoping. You had to say `explicit enum class` to get it to forbid implicit conversions.

N2347(PDF) seems to be the final paper, with the functionality like we have today.

Neither ever considers changing the rules about explicit conversion of enums.

Nevin Liber

unread,
Jan 9, 2017, 6:42:28 PM1/9/17
to std-dis...@isocpp.org
On Mon, Jan 9, 2017 at 10:45 AM, Vicente J. Botet Escriba <vicent...@wanadoo.fr> wrote:

Do you find it a desirable feature?


Not particularly.  I don't think it happens often enough in practice to warrant a language change, especially as you point out we can still perform the operation when necessary:
 

Note that if the static_cast between two enum classes was not supported we could always do it using two static casts

        EC2 ec = static_cast<EC2>(static_cast<int>(e));

-- 
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Thiago Macieira

unread,
Jan 9, 2017, 7:57:03 PM1/9/17
to std-dis...@isocpp.org
On segunda-feira, 9 de janeiro de 2017 23:45:09 PST Vicente J. Botet Escriba
wrote:
> My question is if you believe this behavior is desirable. I don't. Even
> if enums are integer types they are strong types.
> I would expect them to be able to accept an explicit conversion to the
> underlying type only and not other integer types, and in particular not
> to other strong enums (enum classes)

If you can losslessly convert from A to B and from B to C, then why not allow
the conversion from A to C directly?

Specifically to other integer types besides the underlying one, it would be
simply impossible to do it, as those conversions are implicit already. That
is, given

enum E : int {};

E e {};
int i = e;
ushort u = i; // no error

Then this should be allowed without an error:

ushort u = e;

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Nevin Liber

unread,
Jan 9, 2017, 8:44:21 PM1/9/17
to std-dis...@isocpp.org
On Mon, Jan 9, 2017 at 6:56 PM, Thiago Macieira <thi...@macieira.org> wrote:
On segunda-feira, 9 de janeiro de 2017 23:45:09 PST Vicente J. Botet Escriba
Specifically to other integer types besides the underlying one, it would be
simply impossible to do it, as those conversions are implicit already. That
is, given

enum E : int {};

        E e {};
        int i = e;
        ushort u = i;   // no error

Then this should be allowed without an error:

        ushort u = e;

Please no.  That potentially loses information.  While that ship has sailed long ago for the built in types, adding more types which do this just adds more problems.  Can != Should.
Reply all
Reply to author
Forward
0 new messages