4

Assuming there is an enum like this:

enum foo: int {
    first,
    second
}

Then I use it as follows:

foo f(1); // error: cannot initialize a variable of type 'foo' with an rvalue of type 'int'
foo f = foo(1); // OK !

I was wondering what is the difference between the two ? I understand that the second version can be seen as a functional-style cast but why does this make any difference ?

For example, if I do this:

class Bar {};
Bar b = Bar(1); // no matching conversion for functional-style cast from 'int' to 'Bar'

I obviously get an error which makes sense. Therefore, this leads me to believe that in order for the second version of the foo example above to work there must be a conversion from int to enum defined somewhere but if there is such a conversion then why do I get an error in the first version ?

I do apologize if this is a duplicate. I am suspecting it is. This seems relevant: Is this a cast or a construction? ... but not quit.

Thanks in advance !

Than21
  • 321
  • 1
  • 2
  • 10
  • Related, possible duplicate: https://stackoverflow.com/q/28002/1896169 . Function style casts are C-style casts, so `foo f = foo(1);` is the same as `foo f = (foo) 1;`. Note that `foo f = static_cast(1);` compiles – Justin Apr 27 '18 at 20:58
  • I seee. I guess my real question is why is a cast different to the first version ? According to an answer here: https://stackoverflow.com/questions/7612006/is-this-a-cast-or-a-construction a cast is also a "construction" in a sense, right ? – Than21 Apr 27 '18 at 21:02
  • See [scoped enumerations](http://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) on cppreference. `enum class`s have implicit conversions turned off – Justin Apr 27 '18 at 21:04

1 Answers1

3

Yes, the two forms are quite different, in a subtle way. Let's look at the first one, which results in an error. It's initialization of f, of type foo, from an int. It's described here, emphasis mine:

[dcl.init]/17.8

Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. Standard conversions will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered. If the conversion cannot be done, the initialization is ill-formed.

The pertinent conversions in this case are integral conversions, mainly the one specified by the following:

[conv.integral]/1

A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type.

So a an unscoped enumeration can be converted to an integer implicitly, but the converse is not true. Which is why the initialization is ill-formed. However, that functional-style cast notation is essentially a static cast. And a static cast can perform the inverse of (almost) any valid standard conversion. So the casted 1 is then used to initialize f, but at this point we are copy-initializing from a foo prvalue, which is of course perfectly fine.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Yes, that was my understanding as well. However, I am not sure I understand why the cast is different to begin with ? Perhaps the compiler implements casts differently ? But are we still performing a construction operation somehow ? For example, if we do a functional-style cast to some class Bar like `Bar b = Bar(...);` ... are we in-fact calling its constructor ? – Than21 Apr 27 '18 at 21:13
  • @Than21 - A cast is an explicit conversion by the programmer. Some conversions, due to safety concerns, the language demands you do explicitly. When you do an innocent initialization, it'd be really bad if some potentially unsafe conversion happened, because implicit conversions are hard to spot. So the compiler won't do one for you, as the rules reflect. – StoryTeller - Unslander Monica Apr 27 '18 at 21:21
  • Yes, however ... if I have this: `class Bar {}` and then do this: `Bar b = Bar(1);` then I get this: `no matching conversion for functional-style cast from 'int' to 'Bar'` ... which means that there is indeed a functional-style cast from `int` to the `enum` defined somewhere, right ? Or am I missing something ? – Than21 Apr 27 '18 at 21:26
  • @Than21 - The first quote starts with an "Otherwise", because you are not initializing a class object, or casting to a class type. Those are governed by a different set of rules, of which there is too much to bring into the comment section. – StoryTeller - Unslander Monica Apr 27 '18 at 21:28
  • @StoryTeller Aha, does that mean that there are different rules for functional-style casts when it comes to primitive types (compared to "class types") ? – Than21 Apr 27 '18 at 21:33
  • @Than21 - Nor so much functional-style casts, as casts in general. For instance, if you search the static cast section of the standard (http://eel.is/c++draft/expr.static.cast) for "class", you'll see quite a few highlights. – StoryTeller - Unslander Monica Apr 27 '18 at 21:37
  • @StoryTeller I seeee ... it is starting to make sense now. Thanks ! – Than21 Apr 27 '18 at 21:44