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
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
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:
ReplyDeletestruct 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
@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@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.
ReplyDeleteHowever, 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!
@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@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).
ReplyDeletevoid* 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!
@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@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.
ReplyDeleteThanks for checking the C++11 and C++14 Standards on this issue!