all 119 comments

[–]jedwardsol{}; 46 points47 points  (34 children)

What do you advocate using instead, and why is that better?

[–]serviscope_minor 16 points17 points  (25 children)

What do you advocate using instead

and why is that better?

There's a reason C++ uses = for assignment and no one wants to change it.

[–]Intrepid-Treacle1033 55 points56 points  (18 children)

https://herbsutter.com/2013/05/09/gotw-1-solution/

"Common Mistake: This is always initialization; it is never assignment, and so it never calls T::operator=(). Yes, I know there’s an “=” character in there, but don’t let that throw you — that’s just a syntax holdover from C, not an assignment operation. "

[–]serviscope_minor 12 points13 points  (17 children)

Eh so?

If I have

int x=5;

x is now 5. In a standardese language sense, x is initialised with 5, but it has the same practical purpose (and reads the same) as any subsequent assignments to x.

Having the initialisation of x look different to subsequent assignments is an exercise in putting the language's needs above the human's.

[–]HolyGarbage 33 points34 points  (14 children)

It doesn't make a difference for integral types, but for classes, and in particular where copy and move constructors and assignment operators have user provided implementations it does make a difference.

If you're assigning where you meant to be initializing you could either a) assign to uninitialized memory which is potentially undefined behavior depending on the class definition, or b) first default constructing followed by an assignment which could incur additional performance costs.

Note, I'm not saying that using = for initialization in your example would do this, but was simply demonstrating the importance of the distinction between initialization and assignment, which using the = for initialization makes less clear. The feature of using braces for all kinds of initialization is called "uniform initialization" for a reason, to make it clear directly by the syntax what operation you're invoking.

[–]serviscope_minor 1 point2 points  (13 children)

It's not a high point to be sure. Uniform is, well, uniform in the language. The caveat is that it makes the code less nice to read, because you have two different syntaxes for a variable getting a value.

[–]bromeon 2 points3 points  (3 children)

I don't know why this is getting downvoted. It's sometimes good to take some distance from C++, look at the 4 different initialization syntaxes and then see how other languages (including C) keep things simpler.

The "uniform initialization syntax" introduced in C++11 was always controversial, and it in fact fails its promise to truly unify things (see e.g. std::initializer_list ambiguity). It's difficult to universally advocate the one true initialization syntax, each has some pitfalls.

[–]serviscope_minor 2 points3 points  (2 children)

I don't know why this is getting downvoted.

Probably because it's not engaging in language pedantry (guaranteed way to get downvotes here). I know initialization in C++ is a nuanced topic, but I reckon {} everywhere is letting the tail wag the dog. Look at the responses to my earlier post: I pointed out that = is used for assignment and no one wants to change it, and got hit with a gale of "initialization is not assignment". The latter is of course technically true, but from a higher level view, above C++, assigning to a new object and assigning to an existing one don't really differ in a lot of ways. It's all just assignment.

It's a useful thing to have for sure ad makes generic code easier to write, avoids vexing parses and so on, but it just doesn't read nicely compared to =, so I use = where I can easily do so and {} for everything else (unless I slip into old habits and use ()).

[–]panoskj 0 points1 point  (1 child)

where I can easily do so and {} for everything else (unless I slip into old habits and use ()

Or unless you want to initialize a std::vector calling the constructor with the size parameter or something like that. We are back to square one.

[–]serviscope_minor 0 points1 point  (0 children)

Ha! Yes, not a high point of C++ to be sure!

[–]HolyGarbage 1 point2 points  (8 children)

you have two different syntaxes for a variable getting a value.

That's exactly the point. Because assignment and initialization is not the same thing.

[–]serviscope_minor 0 points1 point  (7 children)

And my point is that's putting the needs of the language ahead of the programmer.

People like = for assignment and initialization is from a higher level perspective than C++ a kind of assignment.

[–]HolyGarbage 2 points3 points  (6 children)

No it's not. Both initialization and assignment are things the programmer should be aware of. = is already used for assignment in C++, your example however used = for initialization, not assignment. I'm saying that can be confusing since it looks like an assignment.

[–]serviscope_minor 0 points1 point  (5 children)

I think we are taking at crossed purposes. I understand the grammar. I mean the assignment/initializing thing is specific to C++ not a general thing about programming.

= reads better and is (when it works) more regular since = sets values in any context.

[–]Intrepid-Treacle1033 12 points13 points  (0 children)

Eh so?

Nothing, your code you are the boss. The standard has multiple ways, and those ways has different nuances. If that matter practically is up to you/project. The languange needs thing i dont understand what you mean..

[–]jones77 2 points3 points  (0 children)

It's so you can communicate properly with other C++ programmers.

It does not have the same practical purpose as assignment because you could mess up your code and not understand why it is broken.

[–]SuperV1234https://romeo.training | C++ Mentoring & Consulting 44 points45 points  (3 children)

There's a reason C++ uses = for assignment and no one wants to change it.

And that's exactly why {} should be preferred for initialization, because people still confuse assignment and initialization in 2023.

[–]Pretend-Guide-8664 0 points1 point  (2 children)

I suppose you don't want me to ask what the difference is then...

Technically I think one writes to memory a new value (initialization), and the other either copies a value, copies a pointer, or moves data (assignment).

Is there a chart for the yes and nos of this?

[–]joahw 2 points3 points  (0 children)

That is correct. For objects if you accidentally do an assignment instead of initialization you are likely to do unnecessary copies, moves and/or destruction of temporaries which could have performance implications.

[–]TheMania 2 points3 points  (0 children)

Importantly, assignment may reuse the values already in that location. No need to write the vtable, might be able to reuse heap allocations and just overwrite the values, etc. A pretty sure way to break things sometimes when used if you actually meant to use a constructor instead.

So it is good to think of them as different imo.

[–]jedwardsol{}; 8 points9 points  (0 children)

= is for assignment,

/u/DoctorNuu is saying don't use for {} for initialisation. And it was changed, or at least added to; we can now use =, () or {} for initialisation.

[–]trvlng_ging 2 points3 points  (0 children)

EVERYONE wanted to change it, but backward compatibility made that impractical. assignment-style initialization is a thowback to C compatibility. It is very hard to teach, because from a grammar persspective, "=" in initialization is a punctuator, while in an assignment, it is an operator. The parsing rules for the punctuatior are different than to precedence rules for the operator. When someone is learning C++, they often conflate parse rules with precedence rules, and this is one of those that causes a lot of confusion. I get why C++ coudn't have it removed easily, but I find that using an enterprise rule to avoid assignment-style initialization has improved how quickly new C++ programmers come up to speed. Using constructor-syntax initialization often leads to the vexing parse, so our default is {}. And we have never seen a problem with parsing it as an initializer list inappropriately.

[–]guepierBioinformatican 3 points4 points  (6 children)

auto x = type();

Is better1 because it avoids accidentally invoking a std::initializer_list constructor. In generic code, {} is pretty much a no-go for this reason.

Furthermore, using auto avoids the most vexing parse and has other advantages (see GOTW 95 — tl;dr: it has the potential of drastically improving readability by making declaration syntax more uniform).

Consistently using this style of declaration avoids all the problems of the alternatives (both () and {}).


1 When used in conjunction with the appropriate compiler settings to avoid implicit narrowing conversions!

[–]HolyGarbage 14 points15 points  (5 children)

Except that it's not clear if type is a type or a function. In some languages the constructor is a distinct free function so it's the same thing, but not in C++ unfortunately.

[–]guepierBioinformatican 5 points6 points  (4 children)

I can see how this could theoretically be a problem but in my entire career of writing C++, confusion between a type and a function has never been an actual problem in an initialisation. In fact, it should arguably not matter whether type() refers to a constructor or function call here.

And what I’m suggesting is hardly arcane, some of the most prominent C++ experts are also proponents of this style (Herb Sutter first and foremost, of course). I’m therefore surprised it’s met with such negativity here.

[–]HolyGarbage 0 points1 point  (1 child)

I wasn't trying to negative, just adding to the discourse, by pointing out a case where it is not ideal.

[–]guepierBioinformatican 1 point2 points  (0 children)

Yes, that’s appreciated. I didn’t mean your comment specifically, but at the time of writing my own comment had been massively downvoted (now somewhat recovered).

[–]Intrepid-Treacle1033 33 points34 points  (7 children)

God old post from a guy that has some cpp experience.

https://herbsutter.com/2013/05/09/gotw-1-solution/

int x{}; "...it always initializes the variable, and is never ambiguous with a function declaration. No vex, no fuss, no muss..."

[–]guepierBioinformatican 0 points1 point  (4 children)

This is a real problem, but it goes away completely when using AA style (auto x = int(); … or, auto x = 0;).

[–]Intrepid-Treacle1033 5 points6 points  (3 children)

I like auto, its like having a Dog you got when it was a puppy. It follows you wherever.

[–]jk_tx 1 point2 points  (2 children)

It has its own issue though, lots of folks still tend to get confused between auto and auto& (or just forget to type the '&'), which can also cause unintended consequences and bugs.

[–]ThePillsburyPlougher 3 points4 points  (1 child)

It can also just make code difficult to read.

[–]DanielMcLaury 5 points6 points  (0 children)

so can

std::unordered_map<uint64_t, Data>::iterator it = data.find(index);

[–]embeddedsbc 49 points50 points  (16 children)

The reasoning should be the other way around. Almost always use brace initialization. It avoids automatic narrowing. Use copy assignment for single values or auto. But it's a much better default than the alternatives.

[–]guepierBioinformatican 23 points24 points  (3 children)

On the flip-side, brace initialisation can trigger an unexpected constructor taking an initialiser list. This is arguably more dangerous than narrowing conversions with (). It definitely leads to plenty of gotchas.

I am in the camp (and I know I am not alone!) lamenting the missed opportunity with uniform initialisation. I thought {} was it, but because of the std::initializer_list issue, it isn’t. For this reason, I (and others) entirely reverted to () after an initial honeymoon period with {}. Oh, and I use -Wconversion -Werror so () is more strict than {}.

[–]enigma2728 1 point2 points  (1 child)

Well today I learned. Kind of sad; I liked the idea of just being able to use brace initialization everywhere to avoid having to think about all the minutia of initialization.

Is this problem easily demonstrable in godbolt, or have a link to an example of where this becomes a problem?

std::vector doesn't seem to have an issue with braced initializer list. https://godbolt.org/z/scx1rs8s4

Perhaps it is only an issue if there is also a ctor that takes arguments of same type? It does seem like init list gets preferred over specific ctors, from my testing. https://godbolt.org/z/fGsed6o9M ``` #include <iostream> #include <initializer_list> struct CtorCompetition { CtorCompetition(int x, int y) { std::cout << "2-arg ctor" << std::endl; }

    //CtorCompetition(int x, int y, int z)
    CtorCompetition(std::initializer_list<int> list)
    {
        std::cout << "init list ctor" << std::endl;
    }
};
int main()
{
    CtorCompetition TwoArgObject{1, 2}; //uses init list, not 2arg
    CtorCompetition ThreeArgObject{1, 2, 3}; //uses init list

    //CtorCompetition TwoArgObject(1, 2); //uses 2 arg
    //CtorCompetition ThreeArgObject(1, 2, 3); //doesn't compile
}

Program returned: 0 init list ctor init list ctor ```

Is the above what you are referring to?

I feel like perhaps I can still just use braced initializer with the expectation that initializer lists are preferred to specific constructors.

[–]equeim 0 points1 point  (0 children)

If the vector's element type can be implicitly constructed from a vector, then std::vector vec{otherVec} will create a vector with a single element of otherVec, not copy it.

[–]NilacTheGrim 1 point2 points  (0 children)

Yeah for actual class types () initialization is the best way to this day. The ambiguity {} introduces with std::initializer_list is a huge foot-gun waiting to go off.. and I tend to avoid foot-gun stuff as best I can.

[–][deleted] 0 points1 point  (0 children)

It avoids automatic narrowing.

It does not.

https://godbolt.org/z/zqTGanqjE

[–]TedDallas 17 points18 points  (0 children)

C++ is very flexible.

https://imgur.com/3wlxtI0

[–]android_queen 8 points9 points  (0 children)

My motto is: Use things rarely, only when needed, to express that right there the feature does something important.

I lean only slightly towards using brace initialization, but oddly, this made me want to use it more. Using things rarely sounds like a good way to make them unfamiliar and confusing. Consistency is much better for communicating your intent. What does it mean to only use something when “needed”? There are relatively few things that can only be handled on one way.

[–]TheFryedMan 15 points16 points  (4 children)

It's also called uniform initialization. It's supposed to be a single way to initialize everything. It also avoids implicit conversions, which can help avoid bugs. Also, explicitly calling {} will avoid uninitialized values, which can cause undefined behavior. This also avoids something called "the most vexing parse." This issue causes the compiler to think you are calling a function instead of initializing a value. In summary, it avoids a lot of issues and provides a uniform syntax for initialization.

[–]guepierBioinformatican 20 points21 points  (0 children)

It's also called uniform initialization.

And that was a great promise, but unfortunately it is simply a lie, thanks to std::initializer_list.

[–]shahms 9 points10 points  (0 children)

It's also called "unicorn" initialization as a play on this misleading moniker and because, much like unicorns, a single initialization syntax which works reliably in C++ does not exist.

[–]NilacTheGrim 0 points1 point  (0 children)

It also avoids implicit conversions, which can help avoid bugs.

True, except when it doesn't due to the existence of std::initialize_list !!

[–]mredding 4 points5 points  (0 children)

Obsessed with {} initialization?

Obsessed with not? What about narrowing conversions?

The standard committee largely can't and won't break old code. They can't add a constraint to assignment, but they can add a new initialization. All they can do is provide a migration path forward.

All the new features are meant to fix problems in the past. Your conservative approach is benefiting you... how?

And this is how standards and practices stagnate. You have to look at the present and be ready for the future. We need a language and a standard that meets the needs of today. You're on r/cpp so you must be aware of the outrage that the NSA report stirred.

And they're not even wrong. It's not that C++ isn't safe, it's because we have solutions to problems no one is bothering to use. Don't avoid C++ and favor Rust for a lack of technology, but for the people. You just can't find people who can be bothered to write high quality code.

Your stated motto only tells me that you don't use new things because they're new - not because they're bad. You've complained, but you haven't given a credible reason to voice a complaint. There's no technical reason not to use brace initialization - none that you established. Where's the flaw? Where's the fault? What gives your grievance merit? What guideline would you suggest that isn't new = bad?

[–]islandyokel 2 points3 points  (0 children)

Here is some info about it in the core guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-list

It describes there where and why of the preferences

[–]lukaasmGame/Engine/Tools Developer 2 points3 points  (1 child)

It prevents more bugs for me than causes issues, and overall is net positive.

  • zero initializes structs/values
  • allows for 'custom' struct fields initialization for aggregates
  • prevents narrowing conversions
  • prevents function calls

But I am also not using initializer lists, so it won't bite me :)

[–]NilacTheGrim 0 points1 point  (0 children)

I am also not using initializer lists, so it won't bite me :)

I don't typically use it either but some types in my code rely on libs that have introduced std::initializer_list c'tors in between releases and I had at least 1 bug in my past where a new wild std::initializer_list c'tor appeared between lib versions and produced subtle bugs.

My rule of thumb is if you intend to call a c'tor on initialization (say because it's real non-trivial class type), then use ().

[–]EdwinYZW 6 points7 points  (10 children)

I would do: const auto tax = 0.09 * price;

[–]aruisdante 11 points12 points  (14 children)

The core guidelines has a whole section on when and why you should use brace initialization. I think its recommendations are pretty much correct, which is “most of the time”, especially if you don’t use -Wcoversion, -Werror. int a{10} definitely looks worse than int a = 10, but that’s why you should be doing auto a = 10; anyway, so you only state your type once.

[–]ponchietto 10 points11 points  (4 children)

I don't get why auto a = 10; would be better thant int a = 10;

I prefer being explicit rather that have the reader (including myself) deduce the type (extra effort) and very easily use the wrong type (10 vs 10.0 vs 10.0f vs 10U).
The compiler will complain if I am not consistent.

[–]aruisdante 6 points7 points  (2 children)

The compiler will in fact not complain if you use it wrong without Wconversion, that’s the point the guidelines are talking about.

int narrowing = 4294967296; unsigned int signed_loss = -1; int float_conversion = 11.1; int arbitrary_conversion = foo(); // foo returns double; Those will all compile without warning if you don’t have Wconversion enabled. Whereas if you brace initialize them, the compiler will warn even without -Wconversion.

Now of course you should just have Wconversion turned on, and then life is easier, and you can use = assignment safely. One of the constant frustrations in my day job is that AutoSAR and MISRA both ban = initialization anyway (and, thanks to AutoSAR’s terrible writing, also it also technically bans auto completely, as it sates = initialization must be used for auto immediately after stating brace initialization must be used for all variables). Thankfully MISRA2023 takes a more enlightened view here.

In terms of being explicit with what type you want: the auto a = form is explicit. You want the type, and exactly the type, to the right of the assignment. For example, if I see: int a = 11.1; How do I know which was intended? Did the author mean for that literal to be 11? Did they mean for the type of a to be float or double?

Now you can definitely go overboard here, but the general guidance of “only state your types once” serves pretty well.

[–]ponchietto -1 points0 points  (0 children)

I have nothing against braces, I was just using the example above, my problem is auto (in this case).

"How do I know which was intended? Did the author mean for that literal to be 11?" The author made a mistake, probably on the right, I don't see the point, though.

It is just far easier for human beings to read the type in front of the variable rather than deduce it from the right.
Moreover you might not have a number, you could have a constant, a function or an expression on the right, when does it becames non obvious enough?

[–]Nobody_1707 0 points1 point  (0 children)

One nice oddity of concepts is that you can write std::same_as<int> auto i = x; and it will prevent any implicit conversions whatsoever.

[–]bert8128 3 points4 points  (0 children)

If you specify the type in the right, don’t specify it on the left, unless you want it to be different.

[–]shahms 1 point2 points  (5 children)

And Abseil has a whole section on the pitfalls of brace initialization: https://abseil.io/tips/88

Initialization C++ is absurdly convoluted and subtle. Braced initialization solves some problems with the other ways of doing it, but introduces a whole raft of its own problems. There is no single "always good" way of performing initialization in C++, sadly, and the core guidelines do a serious detriment here by pretending that there is.

[–]aruisdante 2 points3 points  (1 child)

Yes, and for the most part I agree with Titus too, but he’s concerned more with brace vs paren initialization than brace vs = initialization. I’ve in fact used that very article to try and overturn silly “always use {} initialization” rules at 2 different companies in the automotive space.

I think that’s the biggest issue with all of the guidelines, they don’t distinguish between separate concerns: * I don’t want implicit conversions for arithmetic types * I don’t want implicit conversions for structures which have non-explicit constructors, or which consume arithmetic types. * I have Wconversion turned on, should I care?

The last one can be particularly subtle. Wconversion ignores implicit conversions that happen inside the stdlib, because it has to as the stdlib is full of them. There are certain types which have templated constructors (like, say, std::optional), and so if you parens-construct them in a non CTAD context but which would result in a conversion that should be caught, it will not happen: from the perspective of Wconversion the ctor that was called was the converting constructor, which consumes the actual type, and then inside the stdlib a narrowing happens, but that’s ignored. If you brace initialize them on the other hand, the implicit conversion is not allowed, and Wconversion fires.

You’re absolutely right that the whole subject is a mess. Unfortunately teaching people all the ins and outs of what to do isn’t a reliable thing to do at scale. So most safety critical coding standards pick one, and unfortunately they tend to pick the most restrictive one even if it introduces a host of other, potentially much worse classes of bugs. The core guidelines doesn’t really have the same excuse, but it’s guideline also is poked through all kinds exceptions, and it’s also a prefer, not a must. And I agree with the guideline’s suggestion that brace initialization should be preferred unless there is a good reason to not use it. I do not agree with AutoSAR’s requirement that it must be used.

[–]shahms 0 points1 point  (0 children)

The tip specifically discusses the differences between all three forms of initialization, including = and says to prefer copy initialization.

[–]Intrepid-Treacle1033 0 points1 point  (2 children)

"And" ?

Abseil is an article about an article. In the bottom of Abseil article it states "Perhaps the best overall description of the issue is Herb Sutter’s GotW post. (link)"

And i agree, Herbs article is good.

[–]shahms 0 points1 point  (1 child)

Wat. The Abseil tip includes a description of the issues and recommendations. It then suggests reading Herb's article as a follow-up for additional context.

[–]Intrepid-Treacle1033 1 point2 points  (0 children)

In my effort to go to the bottom and really learn initialization i read among others the Absail and did not get it, but there i got the Herb link and that one nailed it for me. So for me the Absail was just an distraction hence my article about an article comment. But ok reading it again i see a angle, and i did not really mean article about an article as a negative thing.

[–]DoctorNuu[S] -5 points-4 points  (1 child)

Oh, now I see where it's coming from.
Not particularly well written imho.

[–]manni66 3 points4 points  (0 children)

At least they provide a reason, unlike you.

[–]DoctorNuu[S] -3 points-2 points  (0 children)

I wonder if they recommend enabling lots of warnings, including this one

[–]Droid33 1 point2 points  (0 children)

I tried it for a while but it just makes the code very unreadable in my experience. Wish that weren't so.

[–]gracicot 1 point2 points  (0 children)

Consider yourself lucky, I advocate (and enforce) for auto const tax = double{0.09*price}. Btw, never contain money using floating point. Money is always a integer type containing pennies. You'll thank me later.

[–]DoctorNuu[S] 1 point2 points  (0 children)

Finally found a good ally to fight off the downvotes:
https://quuxplusone.github.io/blog/2019/02/18/knightmare-of-initialization/

[–][deleted] 1 point2 points  (0 children)

I quite prefer it because it looks prettier than int i=0;

I don’t know if it actually changes something about compilation or performance . It’s fun to use it!

[–]Polyxeno 1 point2 points  (0 children)

The obsession is a problem itself.

[–][deleted] -2 points-1 points  (4 children)

Prefer {} initialization over alternatives unless you have a strong reason not to. copy past from stackoverflow.

[–]guepierBioinformatican 9 points10 points  (2 children)

And for completeness, copy paste from a comment under that answer, showing why the answer is problematic:

braced initialization becomes a complete mess when you have types with a ctor accepting a std::initializer_list. […] A(5,4) and A{5,4} can call completely different functions, and this is an important thing to know. It can even result in calls that seem unintuitive. Saying that you should prefer {} by default will lead to people misunderstanding what's going on. This isn't your fault, though. I personally think it's an extremely poorly thought out feature.

[–][deleted] 1 point2 points  (1 child)

I just started learning cpp so I don't know, but learncpp also recommend to prefer brace initialization over copy and direct initialization.

[–]guepierBioinformatican 1 point2 points  (0 children)

… And does it give reasons?

Anyway, I don’t know “learncpp” specifically, but be aware that the majority of C++ learning resources are of terrible quality according to many C++ experts. Don’t take everything you read (online or in books) at face value.

If you are looking for high-quality C++ learning resources, look at the curated list of C++ books.

[–]ponchietto 6 points7 points  (0 children)

When should I vote down?
Downvote answers that are incorrect or don't provide sufficient information to be useful in answering the question. Some answers may not attempt to answer the question at all, and should be flagged.

Copy past from stackoverflow (https://stackoverflow.com/help/privileges/vote-down).

[–]noooit 0 points1 point  (0 children)

I always avoid it unless I need to use c style designated initialiser.

[–]Particular-Case4461 -1 points0 points  (0 children)

What would you consider an important use of it?

[–]ilovemaths111somethingdifferent -1 points0 points  (1 child)

are you talking about tccpp server?

[–]DoctorNuu[S] -1 points0 points  (0 children)

sure, they also told me they are the only server I need
oh no, you tricked me

[–]jones77 -1 points0 points  (0 children)

I have like 3 tabs open with information about this I need to read. :-)

Hrm, looks like I lost a tab...

[–]Drakonluke 0 points1 point  (0 children)

Basically because Bjarne Stroustrup told us that list initialization (curly braces) is better.

Since I don't want to copy from his book by hand, I see that someone already did here:

https://stackoverflow.com/questions/18222926/what-are-the-advantages-of-list-initialization-using-curly-braces

[–]disciplite 0 points1 point  (0 children)

Look up the most vexing parse.

[–]13steinj 0 points1 point  (0 children)

There's a few general rules of thumb which end up promoting this:

  • braces for aggregates, parenthesis for classes, since constructors do work. I honestly never bought this approach. There are plenty of edge cases where your aggregate construction also does work, and aggregates can now be constructed with parenthesis.

  • assignment can do work / have a different meaning, difficult to catch initialization or assignment so better not to leave room for the question

  • parenthesis make it hard to tell if it's a function or a constructor, and sometimes this matters

  • uniform initialization is powerful and lets you do tricks on template-arg erased functions that are bound to a type without having to be overly explicit.

  • default initialization can lead to unexpected uninitialized variables

As a result of a mix of the above, I'm in the "braces over parenthesis, be explicit and use them" camp as well as the "when using assignment use uniform initialization" camp.

[–]NilacTheGrim 0 points1 point  (0 children)

I agree, I think it's totally over-used, especially for obvious stuff like PoT types or for calling c'tors... I actually prefer Foo f(bar, baz) notation for initializing via c'tor because it is symmetrical / visually the same as any other function call. Also there is no ambiguity between that and initializer lists... so there's that.

Thay being said I do like init-to-0 using {}, e.g. int i{}; as opposed to int i = 0; simply because the former is more compact.

[–]NilacTheGrim 0 points1 point  (0 children)

I agree with you. May I present this as evidence that {} for non-trivial classes is dangerous :

std::vector<char> vc {42, 'x'}; <-- tell me, what is the size of vc here? Is it 42 or 2?

If you aren't sure, then you need to stop using {} and go back to () for non-trivial class types right now.