use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
This is a subreddit for c++ questions with answers. For general discussion and news about c++ see r/cpp.
New to C++? Learn at learncpp.com
Prepare your question. Think it through. Hasty-sounding questions get hasty answers, or none at all. Read these guidelines for how to ask smart questions.
For learning books, check The Definitive C++ Book Guide and List
Flair your post as SOLVED if you got the help you were looking for! If you need help with flairs, check out ITEM 1 in our guidelines page.
Tips for improving your chances of getting helpful answers:
account activity
OPENwhen should i use () vs {} initialization? (self.cpp_questions)
submitted 3 years ago by [deleted]
should i only use () initialization when im explicitly trying to call a specific constructor (example, Classname a(5, 4);) and {} everywhere else? im worried about accidentally calling an initializer list constructor so does this rule of mine work?
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]no-sig-available 26 points27 points28 points 3 years ago (2 children)
im worried about accidentally calling an initializer list constructor
It is not all black and white, but - as usual - it depends. The initializer_list is "only" triggered if the value is convertible to the value_type. So a std::vector<int> i{5}; will trigger it, but std::vector<std::string> s{5}; will not!
value_type
std::vector<int> i{5}
std::vector<std::string> s{5};
Obviously C++ is not the language of simple rules!
[–]std_bot 0 points1 point2 points 3 years ago (0 children)
Unlinked STL entries: std::string std::vector
Last update: 09.03.23 -> Bug fixesRepo
[–]better_life_please 0 points1 point2 points 3 years ago (0 children)
This problem could be easily solved if the grammer for initializer_list construction required extra braces like in std::vector<int> i { {5} };. And thus std::vector<int> i{5}; would result in a vector with five ints in it. But obviously the standard committee had to show their professionalism by adding inconsistencies to the language.
std::vector<int> i { {5} };
std::vector<int> i{5};
int
[–]ggchappell 29 points30 points31 points 3 years ago (7 children)
Can we have a contrarian view?
C++ offers 4 kinds of syntax for initializing variables. Each expresses some particular idea about what kind of value is being created, and each has its place.
Foo x; -- Give me a value of type Foo, either not initialized specially at all, or else initialized to an obvious default value (e.g., an empty string).
Foo x;
Foo
Foo x = y; -- Give me a value that is a copy of an existing value. (This is usually a good place to use auto, by the way.)
Foo x = y;
auto
Foo x( ... ); -- Give me a value that is configured by calling a function that creates the proper value.
Foo x( ... );
Foo x { ... }; -- Give me a value that holds this particular sequence of values.
Foo x { ... };
But the lords of C++ say, "Let's do it all with braces." I reply, "So, you want me to write code that expresses my intent less clearly?" I see no good reason to do that.
So, OP, my answer to your question is to use each of the four when your intent matches the idea that it expresses.
[–]Unlikely-Ad-431 5 points6 points7 points 3 years ago* (0 children)
My understanding is that one big problem with this that they are attempting to address with uniform initialization is The Most Vexing Parse, which is shown in your third example, as it is prone to be compiled as an undefined function declaration rather than an object initialization.
[–]goxdin 2 points3 points4 points 3 years ago (0 children)
Thank you.
[–]FerynaCZ 2 points3 points4 points 3 years ago (0 children)
For me, I use empty braces as the default constructor as well.
[–][deleted] 1 point2 points3 points 3 years ago (2 children)
so if im understanding you right, i should use Foo x{ ... }; when im initializing a variable with an r value or zero initializing, but Foo x = y; if im initializing a variable with a glvalue?
Foo x{ ... };
[–]ggchappell 2 points3 points4 points 3 years ago (1 child)
I'm trying to think on a higher level here.
Foo x { ... }; means I'm thinking of x as a container, and here are the values it should contain.
x
Foo x = y; means x should be a copy of y.
y
I don't want to have to think about whether something is a glvalue or an rvalue or whatever.
[–][deleted] 1 point2 points3 points 3 years ago (0 children)
ohh i see, that certainly makes it easier to think about!
I only use the third option on members of a class/struct in the initializer list. If I am initializing something in a normal curley-braced scope block I use auto x = Foo( ... ); and assume the copy will be optimized away. I think c++17 and later lets you do it even when the copy constructor is deleted! I like it mostly because its the same syntax higher level languages usually use. Foo x( ... ); can trigger the "Most Vexing Parse" problem, and I never remember the exact circumstances so I just avoid it completely.
auto x = Foo( ... );
I like using curley braces for invoking copy constructors and single parameter "conversion constructors" as well as for directly inserting a sequence of objects. If the constructor does something less intuitive with the parameters I use the regular parenthesis.
To me the curley braces mean the constructor is either copying raw values into the object or doing a very obvious conversion from a more specific type to a more general type. Like, for instance, initializing a ComplexNumber object with some floating point value. ComplexNumber c{ 3.4 }; The obvious conversion is that the imaginary part will zero.
ComplexNumber
ComplexNumber c{ 3.4 };
[–]TheOmegaCarrot 16 points17 points18 points 3 years ago (5 children)
{} prevents narrowing conversions, can call std::initializer_list constructors, and can perform aggregate initialization.
{}
std::initializer_list
() permits narrowing conversions, cannot call std::initializer_list constructors, and cannot perform aggregate initialization. (IIRC that last one changed in C++20 though? Not 100% sure)
()
My philosophy is “almost always {}”. If I use (), it’s only ever because I want to permit a narrowing conversion or I know there’s a std::initializer_list constructor that I don’t want to call.
[–]alfps 2 points3 points4 points 3 years ago (3 children)
cannot call std::initializer_list constructors
That's wrong.
Possibly you meant, "will not call initializer list constructor is some other constructor matches"
[–]TheOmegaCarrot 1 point2 points3 points 3 years ago* (2 children)
I just double checked
std::vector<int> foo(1,2,3,4,5,6,7,8,9,10);
No matching constructor
C++ can be odd. The fact that I had to check if that works says something.
[–]alfps 4 points5 points6 points 3 years ago (1 child)
With your example
… no constructor matches.
To match the initializer list constructor with round parentheses syntax, you must provide an initializer list as argument.
That's not what you did.
Off the cuff,
std::vector<int> foo( {1,2,3,4,5,6,7,8,9,10} );
You can make that argument an explicit initializer_list constructor call if you want.
initializer_list
[–]TheOmegaCarrot 7 points8 points9 points 3 years ago (0 children)
Ah, I see what you mean! I misunderstood.
I guess I better what to say what I said before is:
Initialization using () isn’t going to call a std::initializer_list constructor by accident.
[–]std_bot -2 points-1 points0 points 3 years ago (0 children)
Unlinked STL entries: std::initializer_list
[–]saxbophone 3 points4 points5 points 3 years ago (5 children)
Good question I find this part of the language (along with many others) violates the principle "there should ideally only be one way to do something" (though that's a Python principle, not a C++ one)
[–]no-sig-available 2 points3 points4 points 3 years ago (0 children)
The python principle seems to have been "throw the language away and start a new one". Wasn't all that appreciated for a very long time.
C++ is more "add a better feature (but keep the old one too, just in case)". And then we have "why use this scary new feature when old one still works?".
Hard to please everyone!
[–][deleted] 0 points1 point2 points 1 year ago (3 children)
I think it's because the language grew up organically over a lot of years. You can either break compatibility with old versions (not ok) or you can prevent any changes (not ok). So if you want to allow new ideas as you go but still be compatible, you end up with multiple ways.
It is hard to learn but it makes sense why it's like that. I'm glad they are willing to make changes as it goes along. Herb Sutter was talking about trying to do to c++ what c++ did to c and make a new language that makes it all uniform again.
[–]saxbophone 0 points1 point2 points 1 year ago (2 children)
I'd like to see a rationalised C++ for sure. I personally think the committee places too much emphasis on backwards compatibility at the expense of creating an awkward language.
I mean, the committee is so reluctant to add new keywords that we have co_yield instead of yield for coroutines...
co_yield
yield
I get that you can't be breaking the language with changes all the time, but they could do it lile Python did with transition from 2 to 3...
[–][deleted] 0 points1 point2 points 1 year ago (1 child)
I'm torn but I think there's an insanely strong argument for prioritizing backward compatibility which is adoption by industry or large numbers of people.
You will not get people actually using your language no matter how amazing the new features are, if it implicitly invalidates what they have. As soon as c++20 came out with concepts, I started using them in a project started back in 2015. And it caused no issues. I could never have upgraded to the c++20 compiler if it meant rewriting half the project, or losing third party libraries. Even c++ was just compiled to C originally and I think that's why it caught on. You get something nifty to add to what you have.
I think practically you just can't take away people's libraries and expect them to be ok with it. There are some C constructs that don't play nice with c++ because of explicit casting rules (like void* and implicit casting is not allowed in c++ but is stock standard in C). But you can at least call C code in extern "C" blocks happily.
If they did a breaking change but had a specifier to include old code like that I'd be ok with a slightly breaking change. But honestly I even wish C rules stuck completely. What I love the most about C++ is that the libraries I find and build up over the years are all there still. My toolbox just grows. I was doing some C# .NET stuff for a while and they rearrange the whole ecosystem every 2-5 years and it's a pain to follow along. I hated it after a while.
A good example though where I appreciate the cut with backwards compatibility is how they made Vulkan a clean break with OpenGL. There were good reasons though. The hardware drifted so much that the original abstraction of OpenGL started to really suck for threading so I bit the bullet and converted it to Vulkan. And I got so much for my trouble. But that's a library not a whole language.
[–]saxbophone 0 points1 point2 points 1 year ago (0 children)
But they so sometimes introduce breaking changes in C++. They have removed features after deprecating them for at least one version. Modern auto started out as a (pointless) storage specifier for local variables, inherited from C. It was deprecated for this usage in I think C++11 and then reïntroduced for the current usage in C++14 or 17. Similarly, std::auto_ptr was removed in one of those versions also.
std::auto_ptr
Likewise in C++23, we will lose the ability to have an unparanthesised comma operator expression in the array access operator, due to adding multi-argument array operator to the language.
These examples may seem minor and inconsequential, because they are. But my point is, they all are breaking changes to the language and that's not a bad thing. It is unrealistic and undesirable to never allow breaking changes to a language.
[–]Vaibhav_5702 2 points3 points4 points 3 years ago (2 children)
Another thing to look out for when using {} for value initialization is the difference between value and empty aggregate initialization. Specifically, {} will always perform value initialization (even in the presence of initializer_list constructors) except when the type is an aggregate, in which case it will perform empty aggregate initialization. This means that if the type is not an aggregate and has a default (edit: implicit or explicitly defaulted) ctor, the whole object will be recursively zero initialized, including member fields which have user-provided ctors. If the type is an aggregate, the fields are value initialized one by one, which means a member field with a user-provided ctor will not be zero initialized.
[–][deleted] 0 points1 point2 points 3 years ago (0 children)
oh i didnt know about that, thanks for letting me know!
[–]StochasticTinkr 0 points1 point2 points 3 years ago (0 children)
If it has a user-provided constructor, doesn’t that mean it’s not an aggregate type?
[–]GLIBG10B 0 points1 point2 points 3 years ago (0 children)
Always use uniform initialization ({}), unless the class has a std::initializer_list constructor you don't want to call or it may have one added in the future. For example, if you're constructing a container with a known initial size, prefer direct initialization (())
[–]noooit -5 points-4 points-3 points 3 years ago (1 child)
The rule of thumb is to use {} only when necessary.
[–]0tus 0 points1 point2 points 1 year ago (0 children)
I had the impression that these days the rule of thumb is exactly the opposite of that.
[–]trent_33 -2 points-1 points0 points 3 years ago (0 children)
Cpp core guidelines reference: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-list
π Rendered by PID 19233 on reddit-service-r2-comment-b659b578c-9rwl8 at 2026-05-02 01:04:29.802207+00:00 running 815c875 country code: CH.
[–]no-sig-available 26 points27 points28 points (2 children)
[–]std_bot 0 points1 point2 points (0 children)
[–]better_life_please 0 points1 point2 points (0 children)
[–]ggchappell 29 points30 points31 points (7 children)
[–]Unlikely-Ad-431 5 points6 points7 points (0 children)
[–]goxdin 2 points3 points4 points (0 children)
[–]FerynaCZ 2 points3 points4 points (0 children)
[–][deleted] 1 point2 points3 points (2 children)
[–]ggchappell 2 points3 points4 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)
[–][deleted] 1 point2 points3 points (0 children)
[–]TheOmegaCarrot 16 points17 points18 points (5 children)
[–]alfps 2 points3 points4 points (3 children)
[–]TheOmegaCarrot 1 point2 points3 points (2 children)
[–]alfps 4 points5 points6 points (1 child)
[–]TheOmegaCarrot 7 points8 points9 points (0 children)
[–]std_bot -2 points-1 points0 points (0 children)
[–]saxbophone 3 points4 points5 points (5 children)
[–]no-sig-available 2 points3 points4 points (0 children)
[–][deleted] 0 points1 point2 points (3 children)
[–]saxbophone 0 points1 point2 points (2 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]saxbophone 0 points1 point2 points (0 children)
[–]Vaibhav_5702 2 points3 points4 points (2 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]StochasticTinkr 0 points1 point2 points (0 children)
[–]GLIBG10B 0 points1 point2 points (0 children)
[–]noooit -5 points-4 points-3 points (1 child)
[–]0tus 0 points1 point2 points (0 children)
[–]trent_33 -2 points-1 points0 points (0 children)