Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum class initialization with int

I found out some interesting thing when I programming:

enum class Foo {
  FOO_THING,
  FOO_TOO
};

int main() {
  Foo foo{1};    // It is OK
  Foo foo2(1);   // It is an invalid
}

Could you tell me, why foo{1} is OK for compiler, and why foo2(1) is invalid ?

Compiler GCC (g++ (Ubuntu 7.3.0-21ubuntu1~16.04) 7.3.0) says:

$ g++ -Wall -std=c++17 foo.cpp

  error: cannot convert ‘int’ to ‘Foo’ in initialization
  Foo foo2(1);

I really want to know underlying mechanics. :)))

Edit: Maybe it is some compiler bug...

like image 330
BartekPL Avatar asked Jun 06 '18 10:06

BartekPL


2 Answers

To understand the reasons why the two syntax are not both legit you must consider that scoped enums were introduced with standard c++11 to enforce static type checking and have scoped identifiers (i.e. no name pollution anymore).

Foo foo(1) is not working because implicit conversion from integer type to scoped enum is forbidden, otherwise you lose the benefit of scoped enums, and to avoid conflicts during overload resolution.

When using Foo foo{1} you are using list initialization that was introduced with c++11 too, but got an upgrade with c++17, that consist in implicit conversion from int value to enum as reported here, if a set of requirements are satisfied:

Both scoped enumeration types and unscoped enumeration types whose underlying type is fixed can be initialized from an integer without a cast, using list initialization, if all of the following is true:

  1. the initialization is direct-list-initialization

  2. the initializer list has only a single element

  3. the enumeration is either scoped or unscoped with underlying type fixed

  4. the conversion is non-narrowing.

This makes it possible to introduce new integer types (e.g. SafeInt) that enjoy the same existing calling conventions as their underlying integer types, even on ABIs that penalize passing/returning structures by value.

This syntax is safe and will not interfere with legacy code (written before c++11) because both scoped enums and list-initialization did not exist at the time. Furthermore, as reported in the quote, this enables the use of new integer types (like those of the SafeInt library) without the need to force a static cast for enum types in code that conforms to modern c++ syntax.

like image 167
Gabriella Giordano Avatar answered Oct 19 '22 07:10

Gabriella Giordano


C++17-specific documentation has the following for braced initializer

Otherwise, if T is a enumeration type that is either scoped or unscoped with fixed underlying type, and if the braced-init-list has only one initializer, and if the conversion from the initializer to the underlying type is non-narrowing, and if the initialization is direct-list-initialization, then the enumeration is initialized with the result of converting the initializer to its underlying type.

So foo seems to be conforming valid C++17, but foo2 being not braced initialized is not valid.

like image 41
acraig5075 Avatar answered Oct 19 '22 08:10

acraig5075