We all know and love the new C++11's uniform initialization feature (aka curly braces initialization), and, to be frank, there is much to warrant this love, like:
with structs
struct X { bool x{false}; int i; ...}; X x = { true, 100, ... }; X x{ true, 100, ... };with arrays:
struct Y { int arr[3], bool b, ...}; Y y{ {0,1,2}, false, ... }; int* intArray = new int[3]{1, ,2, 3};with library classes:
std::vector<int> a = { 1, 2, 4 }; // yesss! At last! QMap<QString, QVector<int>> a = { {"0x111", { 1, 1, 1} }, {"0x100", { 1, 0, 0} } };and you can enable it for you own classes as well, writing a constructor taking std::initializer_list as argument (example missing, but you know what I mean...)!
because it's universal, you can use it for types too:
int i{1}; int j{};but it's a bit redundant here, as we already could do:
int i(1); int j(0); // not j()! I never used () but assumed it to be the default initialized int :(but there are 2 additional goodies packed into that, as I learned recently, namely:
1. The first one
— marek krajewski (@mrkkrj) August 22, 2016this is an old problem: unexpectedly, the compiler will consider this:
TimeKeeper time_keeper();not to be an object instantiation but a function definition! Here TimeKeeper is a class (reused) from the Wikipedia article:
class TimeKeeper { public: TimeKeeper(); int get_time(); };This the compiler will balk at:
int t = time_keeper.get_time();But thanks to uniform initialization not at this:
TimeKeeper time_keeper1{}; int t = time_keeper1.get_time();OK, you are right, that's not the most vexing parse 😉, but simply incorrect usage of the constructor! The most vexing parse requires a parameter to the constructor! But I made this error several times myself when blindly typing ahead... none the less, the problem is the same, only with a parameter:
TimeKeeper time_keeper(Timer());Here is a function with taking a function(!) like Timer mkTimer() as single, unnamed (!) parameter. Vexing? Now correct that with a single stroke (or two):
TimeKeeper time_keeper{Timer()};Nice to know when you need a workaround for a vexing parse!
2. The second one
Here Bjarne himself explains that:C++ FAQ: "However, in C++11, {} initialization doesn't narrow..." https://t.co/nc2xYfsQrQ #cpp11— marek krajewski (@mrkkrj) September 29, 2016
int x = 7.3; // Ouch!but
int x0 {7.3}; // error: narrowing int x1 = {7.3}; // error: narrowingMoreover, compiler will automatically check int sizes on initializing (in Bjarne's words again):
char c1{7}; // OK: 7 is an int, but it fits in a char char c2{77777}; // error: narrowing (assuming 8-bit chars)That's nice.
Considered I am a traditionalist and like my code to look like a old, regular C++, but these features make a nice argument in favor of using curly braces instead of the normal ones! Will for sure consider that!
Actually
ReplyDeleteint j{}; // (1)
is not the same as:
int j(); // (2)
Since (2) is also an example of the "most vexing parse" you're talking about later in your post.
Try:
int j();
j = 2;
@Anonymous - vexing, vexing, from my old days I remembered this to be a default initialized (i.e. zeroed) integer. Will check that. Thanks!
ReplyDeleteThis is one situation I find it neat:
ReplyDeletestd::ifstream source("myfile.dat", std::ios::binary);
std::vector data(std::istreambuf_iterator(source), {});
For context of this code check:
http://stackoverflow.com/questions/4423361/constructing-a-vector-with-istream-iterators
My answer using this is listed there, but it's not the accepted one.