In C++11, we have that new syntax for initializing classes which gives us a big number of possibilities how to initialize variables.
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
}
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}
For each variable I declare, I have to think which initializing syntax I should use and this slows my coding speed down. I'm sure that wasn't the intention of introducing the curly brackets.
When it comes to template code, changing the syntax can lead to different meanings, so going the right way is essential.
I wonder whether there is a universal guideline which syntax one should chose.
I think the following could be a good guideline:
If the (single) value you are initializing with is intended to be the exact value of the object, use copy (=) initialization (because then in case of error, you'll never accidentally invoke an explicit constructor, which generally interprets the provided value differently). In places where copy initialization is not available, see if brace initialization has the correct semantics, and if so, use that; otherwise use parenthesis initialization (if that is also not available, you're out of luck anyway).
If the values you are initializing with are a list of values to be stored in the object (like the elements of a vector/array, or real/imaginary part of a complex number), use curly braces initialization if available.
If the values you are initializing with are not values to be stored, but describe the intended value/state of the object, use parentheses. Examples are the size argument of a vector or the file name argument of an fstream.
I am pretty sure there will never be a universal guideline. My approach is to use always curly braces remembering that
Initializer list constructors take precedence over other constructors
All standard library containers and std::basic_string have initializer list constructors.
Curly brace initialization does not allow narrowing conversions.
So round and curly braces are not interchangeable. But knowing where they differ allows me to use curly over round bracket initialization in most cases (some of the cases where I can't are currently compiler bugs).
Outside of generic code (i.e. templates), you can (and I do) use braces everywhere. One advantage is that it works everywhere, for instance even for in-class initialization:
struct foo {
// Ok
std::string a = { "foo" };
// Also ok
std::string b { "bar" };
// Not possible
std::string c("qux");
// For completeness this is possible
std::string d = "baz";
};
or for function arguments:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));
For variables I don't pay much attention between the T t = { init }; or T t { init }; styles, I find the difference to be minor and will at worst only result in a helpful compiler message about misusing an explicit constructor.
For types that accept std::initializer_list though obviously sometimes the non-std::initializer_list constructors are needed (the classical example being std::vector<int> twenty_answers(20, 42);). It's fine to not use braces then.
When it comes to generic code (i.e. in templates) that very last paragraph should have raised some warnings. Consider the following:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Then auto p = make_unique<std::vector<T>>(20, T {}); creates a vector of size 2 if T is e.g. int, or a vector of size 20 if T is std::string. A very telltale sign that there is something very wrong going on here is that there's no trait that can save you here (e.g. with SFINAE): std::is_constructible is in terms of direct-initialization, whereas we're using brace-initialization which defers to direct-initialization if and only if there's no constructor taking std::initializer_list interfering. Similarly std::is_convertible is of no help.
I've investigated if it is in fact possible to hand-roll a trait that can fix that but I'm not overly optimistic about that. In any case I don't think we would be missing much, I think that the fact that make_unique<T>(foo, bar) result in a construction equivalent to T(foo, bar) is very much intuitive; especially given that make_unique<T>({ foo, bar }) is quite dissimilar and only makes sense if foo and bar have the same type.
Hence for generic code I only use braces for value initialization (e.g. T t {}; or T t = {};), which is very convenient and I think superior to the C++03 way T t = T();. Otherwise it's either direct initialization syntax (i.e. T t(a0, a1, a2);), or sometimes default construction (T t; stream >> t; being the only case where I use that I think).
That doesn't mean that all braces are bad though, consider the previous example with fixes:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
This still uses braces for constructing the std::unique_ptr<T>, even though the actual type depend on template parameter T.
Related
Is it okay to have a struct that is constructible from a variant (say, from std::variant), and to return such structure implicitly constructed from one of the variant's alternatives?
Consider the following code:
#include <utility>
struct type1_t
{
};
struct type2_t
{
};
struct variant
{
/*template <typename T>
variant(T&& t)
{}*/
variant(type1_t){}
};
struct var_holder_t
{
var_holder_t(variant v)
: m_var(std::move(v))
{}
private:
variant m_var;
};
var_holder_t foo()
{
return type1_t{ }; // <====== offending line
}
MSVC 2017, 2019 allow that, but GCC and clang does not compile. Changing to the other variant's constructor or even using boost::variant or std::variant doesn't help.
Worth noting that changing the offending line to return {type1_t{ }}; makes it compile somehow. Currently I'm lost, though technically speaking adding two extra {} doesn't do much harm readability-wise, probably I'll stick to that at the moment, just wish for an answer.
Also making var_holder_t's constructor template (forwarding) helps, and it makes var_holder_t constructible from type1_t directly (and very likely a lot better performance-wise), but at the moment I want to keep var_holder_t completely non-template - it's supposed (thought, designed) to be casually-written simple code.
Update: what actually confused me is that Clang emits this message:
note: candidate constructor not viable: no known conversion from 'type1_t' to 'variant' for 1st argument var_holder_t(variant v)
Which is weird, because clearly there is a conversion from type1_t to variant, but looks like its just bogus diagnostics. To put it together, I think I can come up with a simple reason why the construction above does not work: it requires two implicit conversions, but the rules are that only one is considered.
The below statement works because list initialization is being performed.
return {type1_t{ }};
copy-list-initialization
return { arg1, arg2, ... } ; (8)
List initialization is performed in the following situations:
...
direct-list-initialization (both explicit and non-explicit constructors are considered)
...
8) in a return statement with braced-init-list used as the return expression and list-initialization initializes the returned object
Here var_holder_t is initialized with a type1_t object and it works as expected.
Given:
struct X {
int m;
std::string s;
};
I can do:
X x; // invokes automatically defined default ctor
X y = { 5 }; // invokes whatever became of the original struct initialization but now maybe runs through C++ initializer-lists?
X z = { 5, "yolo" }; // I assume this is an initializer-list that is being handled by some rule for structs that either runs through a compiler created ctor or copy-from-initializer-list that is similarly compiler-created
and even...
std::vector<X> vx;
vx.push_back({ 99, "yo" }); // okay
But not...
vx.emplace_back(99, "yo"); // error VS 2017 v. 15.7.4
vx.emplace_back({99, "yo"}); // error VS 2017 v. 15.7.4
I'm not understanding the rules between initializer-lists, implicitly defined (or compiler defined) ctors, and forwarding functions like emplace_back()
Would someone be so kind as to either point me to the necessary bits of the standard or a good article on an in-depth discussion of what's become of all of the rules around structs and implicit construction and other compiler-supplied members such as copy / move operators?
I seem to be in need of a more comprehensive rules lesson - because it seems like emplace_back() ought to work for one of either emplace_back(int, std::string), or for emplace_back(initializer-list) - no?
X is an aggregate. While the specific definition of aggregate has changed in every standard, your type is an aggregate in all of them.
List-initialization for an aggregate does aggregate-initialization here. There's no constructor here - there's no "auto" constructor, no synthesized constructor. Aggregate-initialization does not create constructors or go through that mechanism. We're directly initializing each class member from the appropriate initializer in the braced-init-list. That's what both your y and your z are doing.
Now, for the second part. The relevant part of vector looks like:
template <typename T>
struct vector {
void push_back(T&&);
template <typename... Args>
void emplace_back(Args&&...);
};
A braced-init-list, like {99, "yo"}, does not have a type. And you cannot deduce a type for it. They can only be used in specific circumstances. push_back({99, "yo"}) works fine because push_back takes an X&& - it's not a function template - and we know how to do that initialization.
But emplace_back() is a function template - it needs to deduce Args... from the types of its arguments. But we don't have a type, there's nothing to deduce! There are some exceptions here (notably std::initializer_list<T> can be deduced), but here, we're stuck. You would have to write emplace_back(X{99, "yo"}) - which creates the X on the caller's side.
Similarly, emplace_back(99, "yo") doesn't work because emplace uses ()s to initialize, but you cannot ()-initialize an aggregate. It doesn't have a constructor!
Let's say I have a template that stores an object of type T. I want to pass constructor arguments in order to initialize the data member. Should I use uniform-initialization or direct-initialization with non-curly braces?:
template<typename T>
struct X
{
template<typename... Args>
X(Args&&... args)
: t(std::forward<Args>(args)...) // ?
/* or */ : t{std::forward<Args>(args)...} // ?
private:
T t;
};
If the object I want to store is a std::vector and I choose the curly-brace style (uniform-initialization) then the arguments I pass will be forwarded to the vector::vector(std::initializer_list<T>) constructor, which may or may not be what I want.
On the other hand, if I use the non-curly brace style I loose the ability to add elements to the vector through its std::initializer_list constructor.
What form of initialization should I use when I don't know the object I am storing and the arguments that will be passed in?
To be clear the ambiguity arises for types having multiple constructors, including one taking an std::initializer_list, and another one whose parameters (when initialized with braces) may be interpreted as an std::initializer_list by the compiler. That is the case, for instance, with std::vector<int> :
template<typename T>
struct X1
{
template<typename... Args>
X1(Args&&... args)
: t(std::forward<Args>(args)...) {}
T t;
};
template<typename T>
struct X2
{
template<typename... Args>
X2(Args&&... args)
: t{std::forward<Args>(args)...} {}
T t;
};
int main() {
auto x1 = X1<std::vector<int>> { 42, 2 };
auto x2 = X2<std::vector<int>> { 42, 2 };
std::cout << "size of X1.t : " << x1.t.size()
<< "\nsize of X2.t : " << x2.t.size();
}
(Note that the only difference is braces in X2 members initializer list instead of parenthesis in X1 members initializer list)
Output :
size of X1.t : 42
size of X2.t : 2
Demo
Standard Library authors faced this real problem when writing utility templates such as std::make_unique, std::make_shared or std::optional<> (that are supposed to perfectly forward for any type) : which initialization form is to be preferred ? It depends on client code.
There is no good answer, they usually go with parenthesis (ideally documenting the choice, so the caller knows what to expect). Idiomatic modern c++11 is to prefer braced initialization everywhere (it avoids narrowing conversions, avoid c++ most vexing parse, etc..)
A potential workaround for disambiguation is to use named tags, extensively discussed in this great article from Andrzej's C++ blog :
namespace std{
constexpr struct with_size_t{} with_size{};
constexpr struct with_value_t{} with_value{};
constexpr struct with_capacity_t{} with_capacity{};
}
// These contructors do not exist.
std::vector<int> v1(std::with_size, 10, std::with_value, 6);
std::vector<int> v2{std::with_size, 10, std::with_value, 6};
This is verbose, and apply only if you can modify the ambiguous type(s) (e.g. types that expose constructors taking an std::initializer_list and other constructors whose arguments list maybe converted to an std::initializer list)
As with any initialization,
Use braces when the object contains a value, or several values which are getting piecewise initialized. This includes aggregate classes and numbers.
Braces appear more like a list of items.
Use parentheses when the object's initial state is computed from parameters.
Parentheses appear more like a sequence of function arguments.
This general rule includes the case of a container like std::vector<int> which may be initialized with N copies of a number (std::vector<int>(4,5)) or a pair of numbers (std::vector<int>{4,5}).
By the way, since "uniform" initialization isn't really a catch-all, that term is discouraged. The official name is brace-initialization.
I saw a code where a programmer used curly braces to initialize a variable
int var{ 5 };
instead of using the assignment operator
int var = 5;
I know assigning a value to lhs variable using curly braces is a C++11 syntax. Is there any difference between using the two?
Thank you for replies.
They are different kinds of initialization:
T a{b}; // list initialization
T a = b; // copy initialization
T a(b); // direct initialization
There is no difference for ints but there can definitely be differences for other types. For instance, copy initialization might fail if your constructor is explicit, whereas the other two would succeed. List initialization disallows narrowing conversions, but for the other two those are fine.
As far as I know, there is no difference in the two for integers. The {} syntax was made to(however, not limited to, because it is also used for initializer_list) prevent programmers from triggering http://en.wikipedia.org/wiki/Most_vexing_parse, and so instead of std::vector<int> v() to initialize v you write std::vector<int> v{};.
The {} has different behaviours depending on the usage, it can be a call to constructor, a initializer list and even a list of values to initialize members of user-defined class in order of definition.
Example of the last:
class Q{
public:
int a;
int b;
float f;
};
int main()
{
Q q{2, 5, 3.25f};
}
It was suggested by a team member that using an intializer like this:
return Demo{ *this };
was better than:
return Demo(*this);
Assuming a simple class like this:
class Demo {
public:
int value1;
Demo(){}
Demo(Demo& demo) {
this->value1 = demo.value1;
}
Demo Clone() {
return Demo{ *this };
}
};
I admit to having not seen the { *this } syntax before, and couldn't find a reference that explained it well enough that I understood how the two options differed. Is there a performance benefit, a syntax choice, or something more?
Your colleague is missing a trick with "uniform initialization", there is no need for the type-name when it is known. E.g. when creating a return value. Clone could be defined as:
Demo Clone() {
return {*this};
}
This will call the Demo copy constructor as needed. Whether you think this is better or not, is up to you.
In GOTW 1 Sutter states as a guideline:
Guideline: Prefer to use initialization with { }, such as vector v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. …
In particular, using braces can avoid confusion with:
Demo d(); //function declaration, but looks like it might construct a Demo
Demo d{}; //constructs a Demo, as you'd expect
The brace syntax will use a constructor that takes an initializer list first, if one exists. Otherwise it will use a normal constructor. It also prevents the chance of the vexing parse listed above.
There is also different behaviour when using copy initialization. With the standard way
Demo d = x;
The compiler has the option to convert x to a Demo if necessary and then move/copy the converted r-value into w. Something similar to Demo d(Demo(x)); meaning that more than one constructor is called.
Demo d = {x};
This is equivalent to Demo d{x} and guarantees that only one constructor will be called. With both assignments above explicit constructors are cannot be used.
As mentioned in the comments, there are some pitfalls. With classes that take an initializer_list and have "normal" constructors can cause confusion.
vector<int> v{5}; // vector containing one element of '5'
vector<int> v(5); // vector containing five elements.
This is just another syntax for calling your copy constructor (actually, for calling a constructor taking what is in the braces as parameters, in this case, your copy constructor).
Personally, I would say it's worse than before simply because it does the same... it just relies on C++ 11. So it adds dependencies without benefits. But your mileage may vary. You will have to ask your colleague.
I must admit that I'd never seen that before.
WikiPedia says this about C++11 initializer lists (search for "Uniform initialization"):
C++03 has a number of problems with initializing types. There are several ways to initialize types, and they do not all produce the same results when interchanged. The traditional constructor syntax, for example, can look like a function declaration, and steps must be taken to ensure that the compiler's most vexing parse rule will not mistake it for such. Only aggregates and POD types can be initialized with aggregate initializers (using SomeType var = {/stuff/};).
Then, later, they have this example,
BasicStruct var1{5, 3.2}; // C type struct, containing only POD
AltStruct var2{2, 4.3}; // C++ class, with constructors, not
// necessarily POD members
with the following explanation:
The initialization of var1 behaves exactly as though it were aggregate-initialization. That is, each data member of an object, in turn, will be copy-initialized with the corresponding value from the initializer-list. Implicit type conversion will be used where necessary. If no conversion exists, or only a narrowing conversion exists, the program is ill-formed. The initialization of var2 invokes the constructor.
They also have further examples for the case where initialiser list constructors were provided.
So based on the above alone: For the plain-old-data struct case, I don't know if there is any advantage. For a C++11 class, using the {} syntax may help avoid those pesky scenarios where the compiler thinks you're declaring a function. Maybe that is the advantage your colleague was referring to?
Sorry for comming late to this discussion but I want to add some points about the different types of initialization not mentioned by others.
Consider:
struct foo {
foo(int) {}
};
foo f() {
// Suppose we have either:
//return 1; // #1
//return {1}; // #2
//return foo(1); // #3
//return foo{1}; // #4
}
Then,
#1, #3 and #4 might call the copy/move constructor (if RVO isn't performed) whereas #2 won't call the copy/move constructor.
Notice that the most popular compilers do perform RVO and thus, in practice, all return statements above are equivalent. However, even when RVO is performed a copy/move constructor must be available (must be accessible to f and defined but not as deleted) for #1, #3 and #4 otherwise the compiler/linker will raise an error.
Suppose now that the constructor is explicit:
struct foo {
explicit foo(int) {}
};
Then,
#1 and #2 don't compile whereas #3 and #4 do compile.
Finally, if the constructor is explicit and no copy/move constructor is available:
struct foo {
explicit foo(int) {}
foo(const foo&) = delete;
};
none of the return statements compile/link.
This is known as list-initialization. The idea is that in C++11, you will have uniform initialization across the board, and avoid ambiguity where the compiler might think you may be making a function declaration (also known as a vexing parse). A small example:
vec3 GetValue()
{
return {x, y, z}; // normally vec(x, y, z)
}
One reason you would want to avoid list-initialization is where your class takes an initializer list constructor that does something different than you would expect.