Calling constructor with braces - c++

Simple question about C++11 syntaxis. There is a sample code (reduced one from source)
struct Wanderer
{
explicit Wanderer(std::vector<std::function<void (float)>> & update_loop)
{
update_loop.emplace_back([this](float dt) { update(dt); });
}
void update(float dt);
};
int main()
{
std::vector<std::function<void (float)>> update_loop;
Wanderer wanderer{update_loop}; // why {} ???
}
I'd like to know, how it can be possible call constructor with curly brackets like Wanderer wanderer{update_loop}; It is neither initializer list, nor uniform initialization. What's the thing is this?

It is neither initializer list, nor uniform initialization. What's the thing is this?
Your premise is wrong. It is uniform initialization and, in Standardese terms, direct-brace-initialization.
Unless a constructor accepting an std::initializer_list is present, using braces for constructing objects is equivalent to using parentheses.
The advantage of using braces is that the syntax is immune to the Most Vexing Parse problem:
struct Y { };
struct X
{
X(Y) { }
};
// ...
X x1(Y()); // MVP: Declares a function called x1 which returns
// a value of type X and accepts a function that
// takes no argument and returns a value of type Y.
X x2{Y()}; // OK, constructs an object of type X called x2 and
// provides a default-constructed temporary object
// of type Y in input to X's constructor.

It is just C++11 syntax. You can initialize objects calling their constructor with curly braces. You just have to bear in mind that if the type has an initializer_list constructor, that one takes precedence.

In addition, braces-constructors do not allow narrowing, similarly to braces-initialization.
Let's take a look at the simple constructor taking and printing an integer value:
class Test {
public:
Test(int i) {
std::cout << i << std::endl;
}
};
While Test test(3.14); compiles and outputting narrowed 3,
Test test{3.14}; would not even compile:
error: narrowing conversion of ‘3.1400000000000001e+0’ from ‘double’ to ‘int’ [-Wnarrowing]
11 | Test test{3.14};
| ^

Related

Regarding struct initialization list in C++ [duplicate]

The following code can pass compiling and will print 0 on the console. I saw similar code in STL. Does type int in C++ have a constructor? Is int() a call of some defined function?
int main()
{
int a = int();
cout << a << endl;
return 0;
}
In this context,
int a = int(); // 1)
it value-initializes a, so that it holds value 0. This syntax does not require the presence of a constructor for built-in types such as int.
Note that this form is necessary because the following is parsed as a function declaration, rather than an initialization:
int a(); // 2) function a() returns an int
In C++11 you can achieve value initialization with a more intuitive syntax:
int a{}; // 3)
Edit in this particular case, there is little benefit from using 1) or 3) over
int a = 0;
but consider
template <typename T>
void reset(T& in) { in = T(); }
then
int i = 42;
reset(i); // i = int()
first, we start with this syntax:
type variableName = type();
this syntax is called value initialization of a variableName, or in other word we say a variableName is zero initialized.
But, what is the meaning of the value initialization.
if the type of a variableName is a built-in/scalar type like (int, char, ...),
value initialization mean that a variableName is initialized with the value zero.
and if the type is a complex type like (classes, structs, ...) mean that a variableName is initialized by calling its default constructor.
int() is the constructor of class int. It will initialise your variable a to the default value of an integer, i.e. 0.
Even if you don't call the constructor explicitly, the default constructor, i.e. int() , is implicitly called to initialise the variable.
Otherwise there will be a garbage value in the variable.

Empty braces magic in initializer lists

Consider the following minimal example:
#include <iostream>
struct X {
X() { std::cout << "Default-ctor" << std::endl; }
X(std::initializer_list<int> l) {
std::cout << "Ilist-ctor: " << l.size() << std::endl;
}
};
int main() {
X a{};
X b({}); // reads as construct from {}
X c{{}}; // reads as construct from {0}
X d{{{}}}; // reads as construct from what?
// X e{{{{}}}}; // fails as expected
}
Godbolt example
I have no questions about a, b and c, everything is rather clear
But I can not understand why d works
What this additional pair of braces in d stands for? I looked up C++20 standard, but I can not find answer easily. Both clang and gcc agree on this code, so it is me who misses something
A nice trick to do to get information about what the compiler does, is to compile using all errors:
-Weverything. Let's see the output here (for d only):
9.cpp:16:6: warning: constructor call from initializer list is incompatible with C++98
[-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~~~~~
X::X(std::initializer_list) is called.
9.cpp:16:8: warning: scalar initialized from empty initializer list is incompatible with
C++98 [-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~
Scalar (int) initialized in inner {}. So we have X d{{0}}.
9.cpp:16:7: warning: initialization of initializer_list object is incompatible with
C++98 [-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~~~
5 warnings generated.
std::initializer_list is initialized from {0}. So we have X d{std::initializer_list<int>{0}};!
This shows us everything we need. The extra bracket is for constructing the initializer list.
Note: If you want to add extra brackets you can by invoking the copy/move constructor (or eliding it), but C++ compilers won't do it implicitly for you to prevent errors:
X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR
Thought I'd just illustrate:
X d{ { {} }};
| | |
construct an | |
`X` from ... an initializer_list |
containing... int{}
The rules for list-initialization are to find an initializer_list<T> constructor and use it if at all possible, otherwise... enumerate the constructors and do the normal thing.
With X{{}}, that is list-initialization: the outermost {}s are the initializer_list and this contains one element: the {}, which is 0. Straightforward enough (though cryptic).
But with X{{{}}}, this doesn't work anymore using the outermost {} as the initializer_list because you can't initialize an int from {{}}. So we fallback to using constructors. Now, one of the constructors takes an initializer_list, so it's kind of like starting over, except that we'd already peeled off one layer of braces.
This is why for instance vector<int>{{1, 2, 3}} works too, not just vector<int>{1, 2, 3}. But like... don't.

How to declare the of size of a data structure in header (C++) [duplicate]

Suppose I have a unique_ptr member object that I want to initialize in-class, see the code below. Why do I have to use uniform initialization (curly braces)? The second declaration spits an error, something like
so.cpp:10:31: error: expected parameter declarator
std::unique_ptr<Foo> upf2(new Foo);
^
so.cpp:10:31: error: expected ')'
so.cpp:10:30: note: to match this '('
std::unique_ptr<Foo> upf2(new Foo); ^
2 errors generated.
And I don't think is a most vexing parse issue, at least I don't believe so.
#include <memory>
class Foo
{
};
class Bar{
std::unique_ptr<Foo> upf1{new Foo}; // works fine
// std::unique_ptr<Foo> upf2(new Foo); // error here
};
int main()
{
Bar bar;
}
Because those are the rules. In-class initialisers must use "braces" or "equals"; in fact, the syntactical element is called a brace-or-equal-initializer.
int equals = 42; // OK
std::unique_ptr<Foo> braces{new Foo}; // Also OK
I don't know why parentheses aren't allowed; perhaps to avoid the possibility of the initialisation looking like a function declaration. It can be annoying when there's a difference between direct and brace initialisation:
std::vector<int> bad(6); // ERROR: parentheses not allowed
std::vector<int> good{6}; // OK but not the same
std::vector<int> ugly = std::vector<int>(6); // OK but ugly
A non-static data member initializer (NSDMI) must use a brace-or-equal-initializer. The ( expression-list ) form of initialization isn't allowed.
As N2756 explains, in order to allow NSDMIs to behave more like traditional constructor member initializer lists, the names inside initializers are looked up in the scope of the entire class. Unfortunately, this means that allowing parentheses initializers would make it impossible to determine whether something is an initializer or a function declaration at the time the declaration is parsed:
// not real code
struct X {
int i(x); // initializer
static int x;
};
struct Y {
int i(x); // function
typedef int x;
};
The paper discussed a couple possible ways to fix this short of banning it altogether ("everything that can be a declaration is a declaration" or "it's not a type unless you say it's a type"), but neither is very appealing, and the potential confusion was deemed to outweigh the benefit of allowing this form of initialization.

Delegating constructors in c++ () or {}

I read this link of Stroustrup with the following code:
class X {
int a;
public:
X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
X() :X{42} { }
X(string s) :X{lexical_cast<int>(s)} { }
// ...
};
My question is about the line:
X() X{42}{}
Is there any differences between parentheses and curly brackets? If there is no differences can I use curly brackets in other function calls as well? Or is it just in constructor delegation?
And at last Why we should have both syntaxes? It is a little ambigous.
() uses value initialization if the parentheses are empty, or direct initialization if non-empty.
{} uses list initialization, which implies value initialization if the braces are empty, or aggregate initialization if the initialized object is an aggregate.
Since your X is a simple int, there's no difference between initializing it with () or {}.
Initialization values can be specified with parentheses or braces.
Braces initialization was introduced with C++11 and it is meant to be "uniform initialization" that can be used for all non-static variables.
Braces can be used in the place of parentheses or the equal sign and were introduced to increase uniformity and reduce confusion.
It is only a syntactical construct and does not result in performance benefits or penalties.

Unique pointer in-class initialization

Suppose I have a unique_ptr member object that I want to initialize in-class, see the code below. Why do I have to use uniform initialization (curly braces)? The second declaration spits an error, something like
so.cpp:10:31: error: expected parameter declarator
std::unique_ptr<Foo> upf2(new Foo);
^
so.cpp:10:31: error: expected ')'
so.cpp:10:30: note: to match this '('
std::unique_ptr<Foo> upf2(new Foo); ^
2 errors generated.
And I don't think is a most vexing parse issue, at least I don't believe so.
#include <memory>
class Foo
{
};
class Bar{
std::unique_ptr<Foo> upf1{new Foo}; // works fine
// std::unique_ptr<Foo> upf2(new Foo); // error here
};
int main()
{
Bar bar;
}
Because those are the rules. In-class initialisers must use "braces" or "equals"; in fact, the syntactical element is called a brace-or-equal-initializer.
int equals = 42; // OK
std::unique_ptr<Foo> braces{new Foo}; // Also OK
I don't know why parentheses aren't allowed; perhaps to avoid the possibility of the initialisation looking like a function declaration. It can be annoying when there's a difference between direct and brace initialisation:
std::vector<int> bad(6); // ERROR: parentheses not allowed
std::vector<int> good{6}; // OK but not the same
std::vector<int> ugly = std::vector<int>(6); // OK but ugly
A non-static data member initializer (NSDMI) must use a brace-or-equal-initializer. The ( expression-list ) form of initialization isn't allowed.
As N2756 explains, in order to allow NSDMIs to behave more like traditional constructor member initializer lists, the names inside initializers are looked up in the scope of the entire class. Unfortunately, this means that allowing parentheses initializers would make it impossible to determine whether something is an initializer or a function declaration at the time the declaration is parsed:
// not real code
struct X {
int i(x); // initializer
static int x;
};
struct Y {
int i(x); // function
typedef int x;
};
The paper discussed a couple possible ways to fix this short of banning it altogether ("everything that can be a declaration is a declaration" or "it's not a type unless you say it's a type"), but neither is very appealing, and the potential confusion was deemed to outweigh the benefit of allowing this form of initialization.