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").
Related
In C++17, consider a case where S is a struct with a deleted default constructor and a float member, when S is initialized with empty braces, is the float member guaranteed by the standard to be zero-initialized?
struct A {
int x{};
};
struct S
{
S() = delete;
A a;
float b;
};
int main()
{
auto s = S{}; // Is s.b guaranteed to be zero?
}
In my opinion, cppreference.com is not clear, saying both that:
If the number of initializer clauses is less than the number of members and basesor initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14) copy-initialized from empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
(from here), which implies that b is guaranteed to be zero
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.
(from here)
which implies that b is not guaranteed to be zero.
There is also a discussion that seems to imply that while not guaranteed, all known compiler zero-initialize anyway:
The standard specifies that zero-initialization is not performed when the class has a user-provided or deleted default constructor, even if that default constructor is not selected by overload resolution. All known compilers performs additional zero-initialization if a non-deleted defaulted default constructor is selected.
Related to Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?
This is a quirk of C++ that is fixed in C++20. In the meantime you can add explicit to the deleted default constructor to force the struct to become non-aggregate, and make your code a guaranteed compile error:
struct A {
int x{};
};
struct S
{
explicit S() = delete;
const A a;
const float b;
};
int main()
{
auto s = S{}; // error: call to deleted constructor of 'S'
}
Because S is an aggregate, S{} will perform aggregate initialization. The rule in the standard about how members are initialized when there are no initializers in the list is basically what you cited:
If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
So for b, that's the equivalent of float b = {};. Per the rules of list initialization, we have to get all the way down to 3.10:
Otherwise, if the initializer list has no elements, the object is value-initialized.
And value initialization will initialize a float to 0.
Consider the code:
struct Foo
{
const char str[] = "test";
};
int main()
{
Foo foo;
}
It fails to compile with both g++ and clang++, spitting out essentially
error: array bound cannot be deduced from an in-class initializer
I understand that this is what the standard probably says, but is there any particular good reason why? Since we have a string literal it seems that the compiler should be able to deduce the size without any problem, similarly to the case when you simply declare an out-of-class const C-like null terminated string.
The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.
struct Foo
{
Foo() {} // str = "test\0";
// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};
Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.
As mentioned in the comments and as answered by #sbabbi, the answer lies in the details
12.6.2 Initializing bases and members [class.base.init]
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a mem-initializer-id (including the
case where there is no mem-initializer-list because the constructor
has no ctor-initializer) and the entity is not a virtual base class of
an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer , the entity is initialized as specified in
8.5;
otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized
12.6.2 Initializing bases and members [class.base.init]
If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given
struct A {
int i = /∗ some integer expression with side effects ∗/ ;
A(int arg) : i(arg) { }
// ...
};
the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or equal-initializer will not take
place. — end example ]
So, if there is a non-deleting constructor, the brace-or-equal-initializer is ignored, and the constructor in-member initialization prevails. Thus, for array members for which the size is omitted, the expression becomes ill-formed. §12.6.2, item 9, makes it more explicit where we it specified that the r-value initializer expression is omitted if mem-initialization is performed by the constructor.
Also, the google group dicussion Yet another inconsitent behavior in C++, further elaborates and makes it more lucid. It extends the idea in explaining that brace-or-equal-initializer is a glorified way of an in-member initialization for cases where the in-member initialization for the member does not exist. As an example
struct Foo {
int i[5] ={1,2,3,4,5};
int j;
Foo(): j(0) {};
}
is equivalent to
struct Foo {
int i[5];
int j;
Foo(): j(0), i{1,2,3,4,5} {};
}
but now we see that if the array size was omitted, the expression would be ill-formed.
But then saying that, the compiler could have supported the feature for cases when the member is not initialized by in-member constructor initialization but currently for the sake of uniformity, the standard like many other things, does not support this feature.
If the compiler was allowed to support what you described, and the size of str was deduced to 5,
Foo foo = {{"This is not a test"}};
will lead to undefined behavior.
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.
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.
What difference between these ways of initializing object member variables in C++11 ? Is there another way ? which way is better (performance) ?:
class any {
public:
obj s = obj("value");
any(){}
};
Or
class any {
public:
obj s;
any(): s("value"){}
};
Thanks.
No, these are not the same.
The difference between them is the same that applies for direct-initialization vs. copy-initialization, which is subtle but often very confusing.
§12.6.2 [class.base.init]:
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. [...]
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
§8.5 [dcl.init]:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.
Initializing a non-static data member on a member-initializer-list follows the rules of direct-initialization, which doesn't create intermediate temporaries that need to be moved/copied (if compiled without a copy-elision), neither the type of the data member must be copyable/movable (even if the copy is elided). In addition, a direct-initialization introduces an explicit context, while a copy-initialization is non-explicit (if a constructor selected for the initialization is explicit, the program won't compile).
In other words, the obj s = obj("value"); syntax won't compile if obj is declared as:
struct obj
{
obj(std::string) {}
obj(const obj&) = delete;
};
or:
struct obj
{
obj(std::string) {}
explicit obj(const obj&) {}
};
As a more tangible example, while the below won't compile:
struct any
{
std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};
this one will:
struct any
{
std::atomic<int> a;
std::atomic<int> b{ 2 };
any() : a(1) {}
};
Which way is better (performance) ?
With a copy-elision enabled both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when the copy-initialization syntax is used (that obj s = obj("value"); is one of).
Is there another way ?
The brace-or-equal-initializer syntax allows one to perform a direct-list-initialization as well:
class any {
public:
obj s{ "value" };
any() {}
};
Are there any other differences?
Some other differences that are worth mentioning are:
Brace-or-equal-initializer must reside in a header file along with a class declaration.
If both are combined, member-initializer-list takes priority over brace-or-equal-initializer (that is, brace-or-equal-initializer is ignored).
(C++11 only, until C++14) A class that uses brace-or-equal-initializer violates constraints for an aggregate type.
With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization.
Both examples are equivalent.
Though only if the type is copyable or movable (check it for yourself) and NRVO is actually done (any halfway decent compiler will do it as a matter of course).
Though if you had many constructors and constructor-chaining were inappropriate, the first method would allow you not to repeat yourself.
Also, you can use that method to define aggregates with defaults different from aggregate-initialization for (some) members since C++14.
They are the same.
Neither is better than the other in terms of performance, and there is no other way to initialise them.
The benefit of in-class initialisation (the first in your example) is that the order of initialisation is implicit. In initialiser list you have to explicitly state the order - and compilers will warn of out-of-order initialisation if you get the ordering incorrect.
From the standard:
12.6.2.5
nonstatic data members shall be initialized in the order they were declared
in the class definition
If you get the order wrong in your list, GCC will complain:
main.cpp: In constructor 'C::C()':
main.cpp:51:9: warning: 'C::b' will be initialized after
main.cpp:51:6: warning: 'int C::a'
The benefit of initialiser lists is perhaps a matter of taste - the list is explicit, typically in the source file. In-class is implicit (arguably), and is typically in the header file.