Default construction of deleted constructor with braced initializer list - c++

Let's say that I want to disable the construction of class, then I can do the following (as per Best style for deleting all constructors (or other function)?
):
// This results in Example being CopyConstructible:
struct Example {
Example() = delete;
};
or
struct Example {
template <typename... Ts>
Example(Ts&&...) = delete;
};
or
struct Example {
Example(const Example&) = delete;
};
where the first example can still be copied if constructed (which the intention is to disable), but the second two will disable almost all methods for creating Example. If I default construct any of the above using an empty braced initializer list, then the instance is successfully constructed. In Meyer's Effective Modern C++ he gives the example (bottom of page 51):
Widget w3{}; // calls Widget ctor with no args
which I would thus expect to fail for the above Example classes i.e:
Example e{};
should not construct since it should call the deleted default constructor. However, it does, is usable, and if defined as the first case above, is also copyable. See live demo. My question is: Is this correct, and if so, why? Also, if this is correct, how do I completely disable the destruction of the class?

From ground up
We will first clarify what it means to initialize an object and how/when/if a constructor is invoked.
The following is my laymen's interpretation of the standard, for simplicity's sake some irrelevant details have been omitted or mangled.
Initializer
An initializer is one of the following
() // parentheses
// nothing
{} // braced initializer list
= expr // assignment expression
The parentheses and braced initializer list may contain further expressions.
They are used like this, given struct S
new S() // empty parentheses
S s(1, 2) // parentheses with expression list as (1, 2)
S s // nothing
S s{} // empty braced initializer list
S s{{1}, {2}} // braced initializer list with sublists
S s = 1 // assignment
S s = {1, 2} // assignment with braced initializer list
Note that we have not yet mentioned constructors
Initialization
Initialization is performed according to what initializers are used.
new S() // value-initialize
S s(1, 2) // direct-initialize
S s // default-initialize
S s{} // list-initialize
S s{{1}, {2}} // list-initialize
S s = 1 // copy-initialize
S s = {1, 2} // list-initialize
Once initialization is performed, the object is considered initialized.
Note that, again, constructors have not been mentioned
List initialize
We will be primarily explaining what it means to list initialize something, as this is the question at hand.
When list initialization occurs, the following is considered in order
If the object is an aggregate type and the list has a single element that is the object's type or is derived from the object's type, the object is initialized with that element
If the object is an aggregate type, the object is aggregate initialized
If the list is empty, and the object has a default constructor, the object is value-initialized (ends up calling default constructor)
If the object is a class type, the constructors are considered, performing overload resolution with the elements of the list
Aggregate
An aggregate type is defined as [dcl.init.aggr]
An aggregate is an array or a class with
-- no user-provided, explicit, or inherited constructors
-- no private or protected non-static data members
-- no virtual functions, and no virtual, private, or protected base classes
Having a deleted constructor does not count towards providing a constructor.
Elements of an aggregate is defined as
The elements of an aggregate are:
-- for an array, the array elements in increasing subscript order, or
-- for a class, the direct base classes in declaration order, followed by the direct non-static data members that are not members of an anonymous union, in declaration order.
Aggregate-initialization is defined as
[...] the elements of the initializer list are taken as initializers for the elements of the aggregate, in order.
Example e{}
Following the rules above the question why Example e{} is legal is because
the initializer is a braced initializer list
uses list initialization
since Example is an aggregate type
uses aggregate initialization
and therefore does not invoke any constructor
When you write Example e{}, it is not default constructed. It is aggregate initialized. So, yes it is perfectly fine.
In fact, the following compiles
struct S
{
S() = delete;
S(const S&) = delete;
S(S&&) = delete;
S& operator=(const S&) = delete;
};
S s{}; //perfectly legal
Turn off construction
Make sure that Example is not an aggregate type to stop aggregate initialization and delete its constructors.
This is often trivial as most classes have private or protected data members. As such, it is often forgotten that aggregate initialization exists in C++.
The simplest way to make a class non-aggregate would be
struct S
{
explicit S() = delete;
};
S s{}; //illegal, calls deleted default constructor
However, as of 2017 May 30, only gcc 6.1 and above and clang 4.0.0 will reject this, all versions of CL and icc will incorrectly accept this.
Other initializations
This is one of the craziest corners in C++, and it was hellish informative to look through the standard to understand what exactly happened. There have been lots of references already written and I will not attempt to explain them.

Related

Program with explicit works in msvc but not in gcc

I learnt about the explicit keyword in c++ and its uses. Then for practice I wrote the following program that works with MSVC but not with gcc and clang. Demo.
class Testing
{
public:
explicit Testing() = default;
};
int main()
{
Testing t[2] = {}; //works with msvc but not with gcc and clang
}
As you can see, the above works with msvc but not with clang and gcc. I am using C++20 and want to know which compiler is correct for this example according to the C++20 standard.
GCC produces the following error:
<source>:8:21: error: converting to 'Testing' from initializer list would use explicit constructor 'constexpr Testing::Testing()'
8 | Testing t[2] = {}; //works with msvc but not with gcc and clang
| ^
The program is ill-formed and gcc and clang are correct in rejecting the program because Testing t[2]={}; is copy-list-initialization and since t is an array it uses aggregate initialization which in turn result in copy initialization of t's member(s) using the empty initializer list {} which fails as the default ctor is explicit(and so Testing::Testing() cannot be used in copy initialization).
This is explained in detail below.
From list initialization:
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
This means that since T in our example is Testing[2] which is an array(and so an aggregate), aggregate initialization will be performed.
Next, from aggregate initialization:
5) For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
5.4) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list.
This means that the array elements(which are not explicitly initialized btw) will be copy initialized from an empty initializer list {}. What this in turn means is that it is as if for each element of the array we're writing Testing array_element_nth = {}; which results in value-initialization as per dcl.init.list#3.5:
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
Next, from value initialization:
To value-initialize an object of type T means:
if T has either no default constructor ([class.default.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
This means that the array element will be default initialized.
So we move onto default initialization:
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type ([class]), constructors are considered.
The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution ([over.match]).
The constructor thus selected is called, with an empty argument list, to initialize the object.
This means that that the constructors will be enumerated and best of them will be chosen with (). So we move onto over.match.ctor:
When objects of class type are direct-initialized, copy-initialized from an expression of the same or a derived class type ([dcl.init]), **or default-initialized, overload resolution selects the constructor.
For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized.
For copy-initialization (including default initialization in the context of copy-initialization), the candidate functions are all the converting constructors ([class.conv.ctor]) of that class.
The argument list is the expression-list or assignment-expression of the initializer.
This means that the default ctor will be used but since this is in copy initialization context, the explicit ctor cannot be used.
Thus, msvc is wrong in accepting the program.
Here is the msvc bug:
MSVC compiles invalid program involving explicit constructor

Initialising member aggregate type without copy-constructor

I need to initilise an member array of a class with a non-default constructor and without using the copy constructor.
I have the following two classes:
class MemberClass
{
public:
MemberClass(int id) { /* Do stuff */ }; // Define non-default ctor
MemberClass(const MemberClass& other) = delete; // Delete copy ctor
~MemberClass() { /* Do stuff */ }; // Overide default dtor
};
class ContainerClass
{
private:
MemberClass mem[2];
public:
ContainerClass(int id)
: mem { {id} , {id} }
{}
};
which upon compiling gives the following error:
error: use of deleted function ‘MemberClass::MemberClass(const MemberClass&)’
: mem { {id} , {id} }
but I cannot figure out a way to initialise the mem array without defining a copy constructor. I've found answers from here and here explaining that copy-elision is occuring and a copy-ctor is needed to compile but should be removed by the compiler. The MemberClass should never be copied, so defining a copy-ctor just for this initialisation seems very awkward and prone to more difficult debugging elsewhere.
If the MemberClass only has a default constructor then there is no issue given by the compiler. Nor is there any issue given if mem is not an array and is just a single MemberClass object. My only issue is with initialising this array with the non-default ctor, and without using copy-ctor.
Weirdly, if I do not define a destructor I don't get any compilation error, which seems like a clue.
Is there a "correct" way to do this sort of initialisation?
I think this is a bug in gcc.
As specified by C++17 [dcl.init.aggr]/3 (this text was substantially the same in C++14):
When an aggregate is initialized by an initializer list as specified in 11.6.4, the elements of the initializer list are taken as initializers for the elements of the aggregate, in order. Each element is copy-initialized from the corresponding initializer-clause. [...] If an initializer-clause is itself an initializer list, the member is list-initialized,
I'll analyze three different cases here:
Case 1: mem { id, id }
Case 2: mem { MemberClass{id}, MemberClass{id} }
Case 3: mem { {id}, {id} }
In Case 1, mem[0] is copy-initialized by the expression id. This is the same sort of initialization as MemberClass x = id;. It is covered by dcl.init/17.6.2 (copy-initialization from expression of a different type).
The behaviour is that the initializer is converted to a prvalue (i.e. MemberClass{id}) which then direct-initializes the target. In C++14 this was ill-formed: although it is a copy elision context, a valid copy constructor must still exist. In C++17 it is well-formed: initializing an object from a prvalue of the same type is the same as initializing the object using the constructor specified by the prvalue (so-called "guaranteed copy elision").
Case 2 is similar to case 1: it uses 17.6.1 (initialization from expression of the same type) and the same analysis for Case 1 applies.
However, Case 3 is different. As per the last bold quote from dcl.init.aggr/3, mem[0] is list-initialized by {id}, i.e. the code should behave the same as MemberClass z {id};. There is no temporary or copy operation even in C++14.
So the correct behaviour is:
C++14 - Case 3 is correct, Case 1 and 2 ill-formed.
C++17 - All cases correct.
The error messages emitted by gcc suggest that it in C++14 mode is treating Case 3, your code, the same as the other two cases. And in C++17 mode it never got the memo about guaranteed copy elision.

C++ direct list initialization with deleted default constructor

I have the following class definition. I included a private x to make sure it is not an aggregate.
class A {
private:
int x;
public:
A() = delete;
A(std::initializer_list<int>) { printf("init\n"); }
};
Now if I initialize this object with A a = A{}, it will say A::A() is deleted. I guess it is trying to call A::A() but it is deleted. If I comment out that line, so A::A() is automatically generated. Then if I run this code, I could see that it is calling A::A(std::initializer_list<int>)!
And more confusing, if I define A() = default, the initialization calls A::A() again.
Can anyone point me to the right direction of understading this behavior? Thanks!
I'm using G++ 6.3.0 with flag -std=c++17.
The behaviour is correct.
First of all:
// Calling
A a = A{}; // equals A a = A();,
which was a convenience uniformization of the list-initialization idiom.
Refer to: https://stackoverflow.com/a/9020606/3754223
Case A:
class A {
private:
int x;
public:
**A() = delete;**
A(std::initializer_list<int>) { printf("init\n"); }
};
As said above, A a = A{} will be ... = A(), which is deleted.
Case B:
class A {
private:
int x;
public:
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case, there's no default constructor provided, since you have your initializer-list constructor be defined. Consequently {} is converted to an empty initializer list implicitely!
Case C:
class A {
private:
int x;
public:
**A() = default;**
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case the empty default constructor is available and A{} is converted back to A(), causing it to be called.
Please refer to the below pages and get a thorough read into it.
http://en.cppreference.com/w/cpp/utility/initializer_list
http://en.cppreference.com/w/cpp/language/value_initialization
Finally:
Using:
A a = {{}};
Would cause that ALWAYS the initializer-list constructor is called, as the outer-curly-brackets denoted the init-list, and the inner a zero-constructed element. -> Non-empty initializer-list... (See stackoverflow link above again!)
And btw, I cannot understand why the question is downvoted, considering that this is a really tricky part...
You have lots of cases listed, so let us go over them one by one.
A() = delete;
A(std::initializer_list<int>) { ... }
Writing A a = A{}; will indeed try to call the deleted default constructor, and your code fails to compile.
What you have above is list initialization, and because the braced-init-list is empty, value initialization will be performed. This selects the deleted default constructor, which makes your code ill-formed.
If I comment out that line, so A::A() is automatically generated
No, this is not the case. There is no implicitly declared default constructor due to the presence of the user provided constructor that takes an initializer_list. Now, A a = A{}; will call the initializer_list constructor, and in this case the initializer_list will be empty.
if I define A() = default, the initialization calls A::A() again
This behaves the same as the first case, except the default constructor is explicitly defaulted instead of deleted, so your code compiles.
Finally,
I included a private x to make sure it is not an aggregate
There's no need for this, defining a constructor makes A a non-aggregate.
List initialization follows a very specific ordering, as laid out in [dcl.init.list]/3. The two relevant bullet points and their relative ordering are highlighted:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate class and [...]
— Otherwise, if T is a character array and [...]
— Otherwise, if T is an aggregate, [...]
— Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is a specialization of std::initializer_list<E>, [...]
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
— Otherwise, if the initializer list has a single element of type E and [...]
— Otherwise, if T is a reference type, [...]
— Otherwise, if T is an enumeration with a fixed underlying type (7.2), [...]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
An empty initializer list for class types with default constructors is a special case that precedes normal constructor resolution. Your class isn't an aggregate (since it has a user-provided constructor), so A{} will attempt to value-initialize A if A has a default constructor. It doesn't matter if that default constructor is accessible, it only matters if it exists. If it exists and is inaccessible (your first case), that's ill-formed. If it exists and is accessible (your third case), then it's invoked. Only if your class does not have a default constructor will the constructors be enumerated, in which case the A(initializer_list<int> ) constructor will be invoked with an empty initializer list (your second case).

How to in-place initialize an array?

How can I initialize an array without copy or move-constructing temporary elements? When the element has an explicitly deleted copy or move constructor, I can initialize the array only if the element has a default ctor or a ctor with all default arguments and I do one of the following: (a) plainly declare the array, (b) direct initialize and zero initialize the array, or (c) copy initialize and zero initialize the array. Neither direct (but not zero) initialization nor copy (but not zero) initialization compiles.
struct Foo
{
Foo(int n = 5) : num(n) {}
Foo(const Foo&) = delete;
//Foo(Foo&&) = delete; // <-- gives same effect
int num;
};
int main()
{
// Resultant arrays for 'a1', 'a2', and 'a3' are two
// 'Foo' elements each with 'num' values of '5':
Foo a1[2]; // plain declaration
Foo a2[2] {}; // direct initialization and zero initialization
Foo a3[2] = {}; // copy initialization and zero initialization
Foo a4[2] {5, 5}; // direct initialization -> ERROR
Foo a5[2] = {5, 5}; // copy initialization -> ERROR
}
Are those 3 ways the only ways to initialize arrays without copying/moving temporary elements?
Do a1, a2, and a3 count as initializations? e.g. a1 is a declaration, but its elements get initial, albeit default, values.
Are any of them bugs? I did this GCC 6.3.0 with C++14 flag.
Why does copy initialization combined with zero initialization work if it is still under the category of copy initialization?
In general, are all array initializations with curly braces just construction of temporary elements (unless elided when there is no deletion of copy or move constructors (or does elision not apply to arrays?)) followed by per-element copy, move, or mix of copy and move construction?
The code declaration Foo a2[2]; declares an array. The only way to initialize an array is via list-initialization (i.e. a brace-enclosed list of zero or more elements), and the behaviour is described by the section of the Standard titled aggregate initialization. (The term aggregate refers to arrays, and classes that meet certain criteria).
In aggregate initialization, the presence of = makes no difference. The basic definition of it is in C++14 [dcl.init.aggr]/2:
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause.
Also, /7:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equal-
initializer, from an empty initializer list (8.5.4).
You can see from this that copy-initialization is always used for each provided initializer. Therefore, when the initializer is an expression, an accessible copy/move-constructor must exist for the class.
However (as suggested by Anty) you can make the initializer be another list. Copy-initialization using a list is called copy-list-initialization:
Foo a6[2] = {{6}, {6}};
When a single Foo is list-initialized, it is not aggregate initialization (since Foo is not an aggregate). So the rules are different to those discussed above. Copy-list-initialization of a non-aggregate class comes under list-initialization, in [dcl.init.list]/3.4, which specifies for Foo that the initializers in the list are matched to constructor arguments using overload resolution. At this stage the Foo(int) constructor will be chosen, meaning the copy-constructor is not required.
For completeness I'll mention the nuclear option:
typename std::aligned_storage< sizeof(Foo), alignof(Foo) >::type buf[2];
::new ((void *)::std::addressof(buf[0])) Foo(5);
::new ((void *)::std::addressof(buf[1])) Foo(5);
Foo *a7 = reinterpret_cast<Foo *>(buf);
// ...
a7[0].~Foo();
a7[1].~Foo();
Obviously this is a last resort for when you can't achieve your goal by any other means.
Note 1: The above applies to C++14. In C++17 I believe the so-called "guaranteed copy elision" will change copy-initialization to not actually require a copy/move constructor. I will hopefully update this answer once the standard is published. There has also been some fiddling with aggregate initialization in the drafts.
In your case you still may use those constructs:
Foo a4[2] = {{4},{3}};
or
Foo a5[2] {{4},{3}};
You can also create a pointer with malloc and then use array syntax on it (if the class is a POD). Ex:
class A {
public:
int var1;
int var2;
public int add(int firstNum, int secondNum) {
return firstNum + secondNum;
}
}
A * p = 0;
while(!p) {
p = (A*)malloc(sizeof(A) * 2);
}
p[0] = {2, 3};
p[1] = {2, 5};
There is also a way to initialize an array as a temporary value, but I forgot how to do that.
You can directly initialize an array of objects if the class is a POD (plain old data). In order for a class to be a POD, it must have no constructors, destructors, or virtual methods. Everything in the class must also be declared public in order for it to be a POD. Basically, a POD class is just a c-style struct that can have methods in it.
I don't have the C++ standard at hand, and citing it would probably be the only way to prove my words. So to answer each of your questions I can only say:
No this is not all. I cannot provide you wit an exhaustive list of possiblities, but I definitely have used the following before:
xx
struct Foo
{
Foo(int n = 5) : num(n) {}
Foo(const Foo&) = delete;
Foo(Foo&&) = delete;
int num;
};
int main()
{
Foo a1[2]; // plain declaration
Foo a2[2] {}; // direct initialization
Foo a3[2] = {}; // also direct initialization
Foo a4[2] { {5}, {5} }; // also direct initialization
Foo a5[2] = { {5}, {5} }; // also direct initialization
}
Brace initalization is not declare-and-copy, it's separate language construct. It might very well just in-place construct the elements. The only situation where i am not sure if this applies is { Foo(5), Foo(5) } initialization, as it explicitly requests creation of temporaries. The { 5, 5} variant is just the same, because in order to initialize an array you need a brace-initalized list of Foo objects. Since you don't create any, it will use the constructor for temporaries to obtain { Foo(5), Foo(5) }. The { { 5 }, { 5 } } variant compiles because compiler knows that it can construct Foo object out of provided { 5 } initializer and therefore needs no temporaries - although I don't know exact standard wording that allows this.
No, I don't think any of these are bugs.
I remember a line in C++ standard that basically says that a compiler can always replace assignment initialization by direct initialization when creating a new variable.
xx
Foo x( 5 );
Foo x { 5 };
Foo x = { 5 }; // Same as above
As I already pointed out above: No, you can in-place initialize the array, you just need a proper element initializers. { 5 } will be intepreted as "an initializer for Foo object", whereas plain 5 will be understud as "a value that can be converted to a temporary Foo object". Initializer lists generally have to contain either a initializer lists for the elements, or items of the exact type of the elements. If something different is given, a temporary will be created.

Use of ":" symbol to define a constructor in C++ [duplicate]

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").