This question already has an answer here:
Member initializer list notation: curly braces vs parentheses
(1 answer)
Closed 22 days ago.
Consider the following code snippet:
struct A {
int x;
int y;
A(int x, int y)
: x(x) // initialization using parenthesis
, y{y} // initialization using curly braces
{}
};
Is there a difference between these two approaches? I couldn't find any information about this on cppreference or other websites.
For clarification: I'm not talking about normal initialization (e.g. int a{3};), I'm just referring to member initializer lists.
Cppreference mentions this here in its page on member initializer lists.
The body of a function definition of any constructor, before the opening brace of the compound statement, may include the member initializer list, whose syntax is the colon character :, followed by the comma-separated list of one or more member-initializers, each of which has the following syntax:
class-or-identifier ( expression-list(optional) ) (1)
class-or-identifier braced-init-list (2) (since C++11)
parameter-pack ... (3) (since C++11)
Initializes the base or member named by class-or-identifier using direct-initialization or, if expression-list is empty, value-initialization
Initializes the base or member named by class-or-identifier using list-initialization (which becomes value-initialization if the list is empty and aggregate-initialization when initializing an aggregate)
Initializes multiple bases using a pack expansion
In your code, x is being initialized using syntax (1), which performs direct initialization. y is being initialized using syntax (2), which performs list initialization.
Related
Consider some type T (for simplicity, you may assume int) and some integral constant N, which we use to define an array like this:
T array[N]{}; // Note the empty braces here!
According to cppreference, value initialization is defined as follows:
This is the initialization performed when an object is constructed with an empty initializer.
But further down it is written:
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
But then a little bit more down, the following statement appears:
if T is an array type, each element of the array is value-initialized;
From my understanding, the first and third quoted statements contradict to the second one.
So my two questions are:
Is the code snippet above a value initialization or an aggregate initialization?
Do the three quoted statements really contradict or am I missing something?
Note: I've seen similar questions here but they all differ a bit in the specifics.
Is T array[N]{} a value initialization or aggregate initialization?
It is list initialization and part of this initialization process involves aggregate initialization as per dcl.init.list. Additionally, it is also direct list initialization as quoted below.
1) List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the initializer-list or designated-initializer-clauses of the designated-initializer-list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.
[Note 1 : List-initialization can be used
(1.1) as the initializer in a variable definition ([dcl.init])
...
— end note]
The above means that T array[N]{} is list-initialization.
Now let's move on to how the elements of the array is initialized which is given in dcl.init.list#3:
3) List-initialization of an object or reference of type T is defined as follows:
3.4) Otherwise, if T is an aggregate, aggregate initialization is performed.
And since in our example T array[N] is an aggregate, the above implies that in our example the whole process of initialization of the array T array[N] involves aggregate initialization.
Finally, from aggregate initialization given below, we will note that each element is copy-initialized from an empty initializer list:
3) When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate. The explicitly initialized elements of the aggregate are determined as follows:
3.3) Otherwise, the initializer list must be {}, and there are no explicitly initialized elements.
The above means that there are no explicitly initialized elements in our example so we move onto dcl.init.aggr#5:
5) For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
5.2) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list.
(emphasis mine)
Essentially, this means that each element of type T of the array will be initialized from an empty initializer list.
Note that this also explains why the following contrived example fails in C++20:
struct T
{
T() = delete;
};
int main()
{
T array[5]{}; //this fails as a consequence of above explanation
}
This question already has an answer here:
Constructor initialization list with empty initialization
(1 answer)
Closed 1 year ago.
Say I have :
class Foo
{
public:
int x;
Foo() : x() {}
}
Would it be UB to read x after the constructor has ran? More specifically, what type of initialization is this, zero, direct or default initialization?
I know if instead we'd have:
Foo() : x(42) {}
x would be direct-initialized to 42 but I'm not so sure for the snippet above and I don't want to get bit by the UB wolf if this turns out to be default-initialized.
what type of initialization is this
x() performs value-initialization:
when a non-static data member or a base class is initialized using a member initializer with an empty pair of parentheses or braces (since C++11);
As non-class type int, x is zero-initialized as 0 at last.
otherwise, the object is zero-initialized.
This question already has an answer here:
Why can't simple initialize (with braces) 2D std::array? [duplicate]
(1 answer)
Closed 2 years ago.
In the following program, I tried to initialise my 2x2-element std::array using the line which has nested list initialisation (currently commented out). MSVC2017 gave me a compiler error with "too many initializers". Why?
I then gave the non-nested list initialisation a go which worked. Why?
This appears to be inconsistent with initialisation of a nested std::vector. See third line of main(). What is going on here please?
#include <array>
#include <iostream>
#include <vector>
int main()
{
//std::array<std::array<int,2>,2> x = {{0,1},{2,3}}; // ERROR: TOO MANY INITIALIZERS
std::array<std::array<int,2>,2> x = {0,1,2,3}; // OK
std::vector<std::vector<int>> y = {{0,1},{2,3}}; // ALSO OK: NOT TOO MANY INITIALIZERS IF A std::vector?
}
In this declaration
std::array<std::array<int,2>,2> x = {{0,1},{2,3}};
you have three nested aggregates. The first pair of values enclosed in braces
{0,1}
is considered by the compiler as an initializer of the second aggregate that is present in the declaration as one sub-aggregate. So the second pair of values in braces
{2,3}
are considered by the compiler as redundant that has no corresponding object.
You could declare the array for example like
std::array<std::array<int, 2>, 2> x = { { {0,1},{2,3} } };
The braces may be elided when an aggregate is initialized. (C++17 Standard, 11.6.1 Aggregates)
12 Braces can be elided in an initializer-list as follows. If the
initializer-list begins with a left brace, then the succeeding
comma-separated list of initializer-clauses initializes the elements
of a subaggregate; it is erroneous for there to be more
initializer-clauses than elements. If, however, the initializer-list
for a subaggregate does not begin with a left brace, then only enough
initializer-clauses from the list are taken to initialize the elements
of the subaggregate; any remaining initializer-clauses are left to
initialize the next element of the aggregate of which the current
subaggregate is an element.
So in this declaration
std::array<std::array<int,2>,2> x = {0,1,2,3};
the braces are elided and the aggregate is initialized as it is described in the quote..
In this declaration
std::vector<std::vector<int>> y = {{0,1},{2,3}};
there is used the constructor of the class std::vector that accepts std::initializer_list as an argument. In this case the constructor builds as many elements of the vector as there are elements in the initializer list.
std::array is a little special. It's usually implemented as a wrapper of built-in arrays. Then for aggregate initialization, you need to add another pair of {}, as
std::array<std::array<int,2>,2> x = {{{0,1},{2,3}}};
On the other hand, std::array<std::array<int,2>,2> x = {0,1,2,3}; works because of brace elision.
If the aggregate initialization uses copy-list-initialization syntax (T a = {args..}), (until C++14)the braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object.
If you specified nested initializer list, you have to specify the initializer list in the right layers. For this case, it should be 3 layers.
I recently realized that in C++11 we can call a delegating initializer-list constructor like
Foo() : Foo{42} // delegate to Foo(initializer_list<>)
Is this syntax correct? It seems to be, although I would have expected to always use parentheses when calling a function, like Foo({42}). The example code below compiles fine in both clang++ and g++
#include <iostream>
#include <initializer_list>
struct Foo
{
Foo() : Foo{42} // I would have expected invalid syntax, use Foo({42})
{
std::cout << "Foo()... delegating constructor\n";
}
Foo(std::initializer_list<int>)
{
std::cout << "Foo(initializer_list)\n";
}
};
int main()
{
Foo foo;
}
I am well aware of uniform initialization, like declaring objects using { }, but did not know we can also call constructors. We cannot call functions though, the following doesn't compile:
#include <initializer_list>
void f(std::initializer_list<int>){}
int main()
{
f{5}; // compile time error, must use f({5})
}
So, to summarize, my question is the following: are there special rules when delegating constructors, that allow for calling a init-list constructor using only braces, like Foo{something}?
Yes, a mem-initializer such as Foo{42} can contain either a parenthesized expression-list or a braced-init-list. This is the case regardless of whether the mem-initializer-id denotes the constructor's class, a base class, or a member: that is, both when the constructor delegates and when it does not. See the grammar in [class.base.init].
Furthermore, the standard specifies ([class.base.init]/7 in C++14) that the initialization by the expression-list or braced-init-list occurs according to the usual rules of initialization. Therefore if the initializer is a braced-init-list then std::initializer_list constructors will be favoured in overload resolution.
I think the rule is pretty clear that you will be allowed to delegate to an initializer list constructor (emphasis mine):
If the name of the class itself appears as class-or-identifier in the
member initializer list, then the list must consist of that one member
initializer only; such constructor is known as the delegating
constructor, and the constructor selected by the only member of the
initializer list is the target constructor In this case, the target
constructor is selected by overload resolution and executed first,
then the control returns to the delegating constructor and its body is
executed.
So by overload resolution, you can call your initializer list constructor just as if you were calling it in 'normal' code because.
However, I don't know of anything that should allow calling a function that accepts an initializer list in the same way that you can call a constructor with one.
Edit: More about constructor rules (Emphasis again mine):
The body of a function definition of any constructor, before the
opening brace of the compound statement, may include the member
initializer list, whose syntax is the colon character :, followed by
the comma-separated list of one or more member-initializers, each of
which has the following syntax
class-or-identifier (expression-list(optional) ) (1)
class-or-identifier brace-init-list (2) (since C++11)
parameter-pack ... (3) (since C++11)
1) Initializes the base or member named by class-or-identifier using
direct initialization or, if expression-list is empty,
value-initialization
2) Initializes the base or member named by
class-or-identifier using list-initialization (which becomes
value-initialization if the list is empty and aggregate-initialization
when initializing an aggregate)
3) Initializes multiple bases using a
pack expansion
So according to #2, it appears it's legal.
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Why should I prefer to use member initialization lists?
(9 answers)
Closed 8 years ago.
I am moving from structural C to OOP C++ and I frequently found a special use of ":" symbol as an operator when declaring/defining constructors in C++. I roughly understood the use of this style but somebody explain me the exact programming technique with this constructor definition.
e.g.: 1
class time_stamp
{
public:
time_stamp(time &t_time)
: m_time(t_time)
{}
~time_stamp()
{
m_time.update(); // as soon as I'm destroyed, update the time
}
private:
time &m_time;
};
e.g.: 2
class threaded_class
{
public:
threaded_class()
: m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
~threaded_class()
{
pthread_mutex_destroy(&m_mutex);
}
/** Some other member declarations */
}
Please explain me use of ":" in below lines of codes from above 2 examples
time_stamp(time &t_time) : m_time(t_time){} and
threaded_class(): m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
The colon character : is used to denote the constructor member initializer list. This is the place where you can initiailze members of a class or call a base class constructor.
C++ Standard n3337 12.6.2 § 3:
A mem-initializer-list can initialize a base class using any
class-or-decltype that denotes that base class type.
C++ Standard n3337 12.6.2 § 7:
The expression-list or braced-init-list in a mem-initializer is used
to initialize the designated subobject (or, in the case of a
delegating constructor, the complete class object) according to the
initialization rules of 8.5 for direct-initialization.
Example:
class Foo {
int a;
};
If you would like integer a to have determined value after call to constructor is made you have to give a this value in constructor. There are two options:
in constructor body
Foo::Foo() {
a = 70;
}
in it's member initializer list
Foo::Foo() : a( 70) {
}
Initialization via a member initilization list should be preferred
It is always legal, is never less efficient than assignment inside the body of the constructor, and is often more efficient. The very important thing about initialization list is that it allows to direct initialize class member omitting a default construction of a member being subject to such a process.
As Scott Myers pointed out in his "Effective C++", if you fail to specify an initialization argument for class member, it's default constructor will be called. When you later perform an assignment to it inside your class constructor, you will call operator= on member variable. That will total two calls to member functions: one for the default constructor and one more for the assignment. You can omit a first call by specifying an initializer. Also as Scott Myers pointed out in his "Effective C++" : "from a purely pragmatic point of view, there are times when the initialization list must be used. In particular, const and reference members may only be initialized, never assigned".
A trap
(At least) Equally important thing is that members are not initialized in order of their appearance in initialization list but in order of declaration in class. Remember this to avoid errors like
/* trying to allocate very large block of memory
as a result of initializing a vector with
uninitialized integer: std::vector<int> v( N)
*/
class SearchEngine {
std::vector<int> v;
int N;
explicit SearchEngine( std::vector<int> const& keys)
: N( keys.size()), v( N), {
C++ Standard n3337 8.5.4 § 1:
List-initialization is initialization of an object or reference from a
braced-init-list. Such an initializer is called an initializer list,
and the comma-separated initializer-clauses of the list are called the
elements of the initializer list. An initializer list may be empty.
List-initialization can occur in direct-initialization or copy-
initialization contexts; list-initialization in a
direct-initialization context is called direct-list-initialization and
list-initialization in a copy-initialization context is called
copy-list-initialization. [ Note: List-initialization can be used — as
the initializer in a variable definition (8.5)
— as the initializer in
a new expression (5.3.4)
— in a return statement (6.6.3)
— as a
function argument (5.2.2)
— as a subscript (5.2.1)
— as an argument to
a constructor invocation (8.5, 5.2.3)
— as an initializer for a
non-static data member (9.2)
— in a mem-initializer (12.6.2)
— on the
right-hand side of an assignment (5.17)
[ Example:
int a = {1};
std::complex z{1,2};
new std::vector{"once",
"upon", "a", "time"}; // 4 string elements
f( {"Nicholas","Annemarie"}
); // pass list of two elements
return { "Norah" }; // return list of
one element
int* e {}; // initialization to zero / null pointer
x =
double{1}; // explicitly construct a double
std::map
anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };
— end example ] — end note ]
It's for member initialisation. This is the only place you can init members without them being default initialised.
If you do it within curly braces, the default constructor for members has already been invoked and your assigning new value to it. With the colon syntax, you decide how members are initialised (in terms of value fortrivial types and in terms of constructor with non trivial ones).
Initialization list. It is useful when you want to initialize the member objects right after construction. And you have to use it when a member object has a not a default constuctor.
And it's not just another way to initialize members, sometimes you have to use it, and most of the time you should use it to keep the code consistence.
Here is an example for a situation that you have to use it:
struct A
{
const X x; // X has not default constructor
A() : x(some_value) {}
};
Even if a member object has a default constructor, you should initialize it by initialization-list to avoid redundant construction.
struct A
{
string x;
A() : x("Hello") {}
};
In the above case, if you assign "Hello" inside the constructor's body, then you made an unnecessary call to string::string() and then x = "Hello";, which it can be replaced by just one call to string::string("Hello").