0

C++11 has given us initialiser lists. I've learned that these do not perform narrowing conversions, which sometimes breaks compilation of existing code, for example when operating on enum values with implicitly int-widened values:

 enum COMMAND { COMMAND_WRITE_MISC_CONFIG = 0x70 }; struct CommandSettings { quint8 buddy; }; void NarrowingTest::testNarrowing() { quint8 i = 100; CommandSettings test{static_cast<quint8>(COMMAND_WRITE_MISC_CONFIG | i)}; quint8 x = COMMAND_WRITE_MISC_CONFIG | i; QVERIFY(true); } 

The initialisation of test wouldn't compile without the cast.

What I'm looking for is the rationale behind the assignment initialisation of x still working.

3
  • Are you sure the above is valid c++03? I'm pretty sure the cast is needed there as well. Commented Jan 8, 2017 at 17:10
  • 1
    @StoryTeller The version with the cast is a syntax error in C++03. If it had been CommandSettings test = {COMMAND_RITE_MISC_CONFIG | i}; though, that's valid C++03 but made an error as of C++11, and can be made valid C++11 by adding a cast. Commented Jan 8, 2017 at 17:29
  • @StoryTeller nope, it's part of a compiling test-app. Just copied out the relevant part (it's a Qt5.7 test-case, with C++11 enabled by default) Commented Jan 8, 2017 at 18:27

1 Answer 1

1
CommandSettings test{static_cast<quint8>(COMMAND_WRITE_MISC_CONFIG | i)}; 

It is an aggregate initialization.

From the reference above:

The effects of aggregate initialization are:

...

If the initializer clause is an expression, implicit conversions are allowed as per copy-initialization, except if they are narrowing (as in list-initialization) (since C++11).


quint8 x = COMMAND_WRITE_MISC_CONFIG | i; 

It is a copy initialization.

From the reference above:

The effects of copy initialization are:

...

Otherwise (if neither T nor the type of other are class types), standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T.

It should allow narrowing conversions at least for backward compatibility.

Sign up to request clarification or add additional context in comments.

1 Comment

Backward compatibility is key. The heritage from C comprises unsafe but workable rules. C++ adds safety, but only with new syntax. Note that = { initializer } is also valid and safe — actually it's the safest, since it disallows both narrowing conversions and conversions by explicit functions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.