Monday, February 16, 2015

New EMC++ Sample Content is Now Online

O'Reilly has updated the Effective Modern C++ Sample Page to include a new book excerpt. This time it's

Item 7: Distinguish between () and {} when creating objects.

This is the published version of an Item I originally posted in draft form in a blog post about 11 months ago.

Enjoy!

Scott

7 comments:

  1. Thanks, Scott! I find your explanation on overload resolution when a constructor has a std::initializer_list parameter very helpful. If I understand correctly, the construction of my Widget 'w', below here, should use the constructor has a std::initializer_list parameter:

    struct String { String(const char*); };

    struct Widget {
    Widget(std::initializer_list < String >);
    Widget(wchar_t, bool);
    };

    Widget w{ wchar_t{}, bool{} };

    Please check!

    Unfortunately, the result appears to depend on the compiler. Visual C++ uses the first constructor, while gcc and clang appear to prefer the second constructor. At least according to the link errors from the compilers I checked online:

    vc++: http://rextester.com/PBPQM72417
    clang: http://rextester.com/ZLHP54276
    gcc: http://rextester.com/HYXQ4265

    ReplyDelete
  2. @Niels Dekker: I'd expect your code to call the non-initializer_list constructor, because there is no conversion from wchar_t or bool to String (because there's no conversion from wchar_t or bool to const char*). Can you clarify why you believe your code should call the constructor taking a std::initializer_list?

    ReplyDelete
  3. @Scott I thought that both wchar_t{} and bool{} might be interpreted as null pointers, because they are both integral constant expressions that evaluate to zero. But apparently, only Visual C++ does so.

    However, GCC does seem to allow wchar_t() and bool() as null pointer (using empty parentheses):

    String s = wchar_t();

    See also http://rextester.com/JRE93899

    Of course, I certainly wouldn't promote using something like wchar_t() as null pointer, I just want be aware of where things can go wrong!

    ReplyDelete
  4. @Niels: In C++14, 8.5/11 says that empty parentheses value-initialize an object (which, for built-in types is zero initialization). 8.5.4/3 bullet 7 seems to say the same thing for empty braces, and that suggests that MSVC exhibits correct behavior. It'd be interesting to know why gcc and clang behave differently.

    ReplyDelete
  5. @Scott It might be that my knowledge of what expressions are acceptable as null pointer is outdated... The following two lines of code are both accepted by Visual C++: http://rextester.com/XCF74748 (MSVC 2013). While they are both rejected by Clang: http://rextester.com/HGOP20526 (clang 3.4).

    void* ptr1 = wchar_t();
    void* ptr2 = wchar_t{};

    GCC accepts the first line of code, but it rejects the second: http://rextester.com/RSP72879 (gcc 4.8.2) So in this context, only GCC appears to distinguish between () and {}... reminding me of the title of Item 7!

    ReplyDelete
  6. @Niels: It would be intellectually interesting to know whether things like wchar_t()/wchar_t{} and bool()/bool{} are supposed to be interpreted as integral constant expressions that evaluate to zero (per C++11 4.10/1) or integral literals with value zero (per C++14 4.10/1), but in practice, anybody deliberately writing code that used them in that way should probably be sent to a reeducation camp :-)

    ReplyDelete
  7. @Scott Will you do the reeducation? ;-) But seriously, I still find it unfortunate that the result of initialization by something like {wchar_t(),bool()} or {wchar_t{},bool{}}, as in my original example, appears compiler dependent. Personally I prefer Clang's behavior here: calling the Widget(wchar_t,bool) constructor in both cases.

    Thanks for checking the C++11 and C++14 Standards on this issue!

    ReplyDelete