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.




Niels Dekker said...

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:


Scott Meyers said...

@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?

Niels Dekker said...

@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

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!

Scott Meyers said...

@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.

Niels Dekker said...

@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++: (MSVC 2013). While they are both rejected by Clang: (clang 3.4).

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

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

Scott Meyers said...

@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 :-)

Niels Dekker said...

@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!