I have some misunderstanding:
Let's mark default constructor of struct A as deleted:
struct A
{
A() = delete;
};
The next instruction is well-formed and what's that effect?:
A a{};
From cppreference value initilization:
1) If T is a class type with no default constructor or with a
user-provided default constructor or with a deleted default
constructor, the object is default-initialized.
but then the effect of default initialization is:
If T is a class type, the default constructor is called to provide the
initial value for the new object.
Or it's aggregate initialization?
Thanks!
Your struct A is :
a class type that has:
no user-provided constructors1,
no private or protected non-static data members,
no base classes,
no virtual member functions.
It therefore qualifies as an aggregate type, according to the definition provided by § 8.5.1/1.
Then comes the priority of aggregate initialization over value initialization. The standard says that aggregate initialization has precedence over value intialization (draft N3936, § 8.5.4/3, page 201) (emphasis mine)
List-initialization of an object or reference of type T is defined as follows:
If T is an aggregate, aggregate initialization is performed (8.5.1).
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
[... more rules...]
(1) As requested in the comments on why a deleted constructor does not count as user-defined, here is what the standard says (draft N3936, § 8.4.2/5, page 198):
A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.
It is well formed. A is an aggregate1, and, according to draft N3936, an empty initializer list used in direct-list initialization of an aggregate results in aggregate initialization:
From § 8.5.4/3 List-initialization [dcl.init.list]:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate, aggregate initialization is performed (8.5.1).
[ Example:
struct S2 { int m1; double m2, m3; };
....
S2 s23{}; // OK: default to 0,0,0
....
— end example ]
....
The relevant changes between C++11 and C++1y are a change in the precedence of aggregate vs. value initialization for the case of aggregates:
C++11 leads with
List-initialization of an object or reference of type T is defined as
follows:
— 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 an aggregate, aggregate initialization is performed (8.5.1)....
followed by the example above.
C++1y gives priority to aggregate initialization:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate, aggregate initialization is performed (8.5.1).
....
— Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
1 Why is A an aggregate?
It is an aggregate both in C++11 and C++14.
C++1y:
8.5.1 Aggregates [dcl.init.aggr]
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
The only part that is not obvious is whether the defaulted constructor is user-provided or not. It isn't:
In § 8.4.2 [dcl.fct.def.default]:
A function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration.
Related
This question already has answers here:
C++11 allows in-class initialization of non-static and non-const members. What changed?
(3 answers)
Closed 3 years ago.
This question is for C++11.
In the following struct A, will x always be 42, when the default constructor is used?
struct A{
A() = default;
private:
int x = 42;
}
In short, I'm wondering if the default constructor guarantees that default member values will be set.
Yes.
Unfortunately the wording below is from the standard draft as of today, but the principle is the same in C++11.
[class.default.ctor]/4 A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]) to create an object of its class type ([intro.object]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer ([class.base.init]) and an empty compound-statement.
[class.base.init]/9 In a non-delegating constructor, if a given potentially constructed subobject 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), then:
if the entity is a non-static data member that has a default member initializer ([class.mem]) and either
the constructor's class is a union ([class.union]), and no other variant member of that union is designated by a mem-initializer-id or
the constructor's class is not a union, and, if the entity is a member of an anonymous union, no other member of that union is designated by a mem-initializer-id, the entity is initialized from its default member initializer as specified in [dcl.init];
[..]
In short, I'm wondering if the default constructor guarantees that default member values will be set.
An example of exactly this follows the passage latterly quoted above.
However, if you were to define A::A() and provide an initialiser for x, it would take precedence over the inline initialiser.
#include<cstddef>
template<typename T, std::size_t N>
struct A {
T m_a[N];
A() : m_a{} {}
};
struct S {
explicit S(int i=4) {}
};
int main() {
A<S, 3> an;
}
The above code compiles fine with MSVC (2017), but fails with clang 3.8.0 (Output of clang++ --version && clang++ -std=c++14 -Wall -pedantic main.cpp):
clang version 3.8.0 (tags/RELEASE_380/final 263969)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
main.cpp:6:15: error: chosen constructor is explicit in copy-initialization
A() : m_a{} {}
^
main.cpp:14:13: note: in instantiation of member function 'A<S, 3>::A' requested here
A<S, 3> an;
^
main.cpp:10:14: note: constructor declared here
explicit S(int i=4) {}
^
main.cpp:6:15: note: in implicit initialization of array element 0 with omitted initializer
A() : m_a{} {}
^
1 error generated.
clang 5.0 also refuses to compile this:
<source>:6:17: error: expected member name or ';' after declaration specifiers
A() : m_a{} {}
^
<source>:6:14: error: expected '('
A() : m_a{} {}
^
2 errors generated.
If I use simple parentheses in As constructor to (i.e. A() : m_a() {}), it compiles fine. From cppreference I would have suspected that both should result in the same (i.e. value initialization). Am I missing something or is this a bug in one of the compilers?
Clang is correct.
Your confusion comes from:
From cppreference I would have suspected that both should result in the same (i.e. value initialization).
No they have different effects. Note the notes in that page:
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.
That means when initialized with braced-init-list, for aggregate type, aggregate-initialization is preferred to be performed. With A() : m_a{} {}, and m_a is an array, which belongs to aggregate type, then aggregate initialization is performed instead:
(emphasis mine)
Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
and
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by 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).
That means, the remaining elements, i.e. all the 3 elements of m_a will be copy-initialized from the empty list; for empty list the default constructor of S will be considered but it's declared as explicit; the copy-initialization won't invoke explicit constructors:
copy-list-initialization (both explicit and non-explicit constructors are considered, but only non-explicit constructors may be called)
On the other hand, A() : m_a() {} performs value initialization, then
3) if T is an array type, each element of the array is value-initialized;
then
1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;
then the default constructor of S is invoked to initialize the elements of m_a. Whether it's explicit or not doesn't matter for default initialization.
For m_a{}:
[dcl.init]/17.1 sends us to [dcl.init.list], and [dcl.init.list]/3.4 says that we perform aggregate initialization on m_a per [dcl.init.aggr].
The semantics of initializers are as follows. [...]
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.
[...]
List-initialization of an object or reference of type T is defined as follows:
[...]
Otherwise, if T is an aggregate, aggregate initialization is performed.
[...]
[dcl.init.aggr]/5.2 says that we copy-initialize each element of m_a from an empty initializer list, i.e., {}.
For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
[...]
Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
[...]
This sends us back to [dcl.init]/17.1 for the initialization of each element, which again sends us to [dcl.init.list].
This time we hit [dcl.init.list]/3.5, which says that the element is value-initialized.
List-initialization of an object or reference of type T is defined as follows:
[...]
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
[...]
Which brings us to [dcl.init]/8.1, which says that the element is default-initialized.
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
[...]
Which hits [dcl.init]/7.1, which says we enumerate constructors per [over.match.ctor] and perform overload resolution on the initializer ();
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated
([over.match.ctor]), and the best one for the initializer () is
chosen through overload resolution. The constructor thus selected is
called, with an empty argument list, to initialize the object.
[...]
and [over.match.ctor] says:
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, the candidate functions are all the converting
constructors of that class.
This default-initialization is in the context of copy-initialization, so the candidate functions are "all the converting constructors of that class".
The explicit default constructor is not a converting constructor. As a result, there is no viable constructor. Hence overload resolution fails, and the program is ill-formed.
For m_a():
We hit [dcl.init]/17.4, which says that the array is value-initialized.
The semantics of initializers are as follows. [...]
[...]
If the initializer is (), the object is value-initialized.
[...]
Which brings us to [dcl.init]/8.3, which says that each element is value-initialized.
To value-initialize an object of type T means:
[...]
if T is an array type, then each element is value-initialized;
[...]
Which again brings us to [dcl.init]/8.1, and then to [dcl.init]/7.1, and so we again enumerate constructors per [over.match.ctor] and perform overload resolution on the initializer ();
This time, the default-initialization is not in the context of copy-initialization, so the candidate functions are "all the constructors of the class of the object being initialized".
This time, the explicit default constructor is a candidate and selected by overload resolution. So the program is well-formed.
This is explicitly ill-formed by the Standard (the question is, though, why?):
m_a{} list-initializes the S::m_a:
[dcl.init.list]/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.
As an array, A<S, 3>::m_a is an aggregate type ([dcl.init.aggr]/1).
[dcl.init.aggr]/3.3
When an aggregate is initialized by an initializer list as specified in [dcl.init.list], [...]
3.3 the initializer list must be {}, and there are no explicitly initialized elements.
following, since there are no explicitly initialized elements:
[dcl.init.aggr]/5.2
For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows: [...]
5.2 if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
Each S of A<S, 3>::m_a is, then, copy-initialized:
[dcl.init]/17.6.3
The semantics of initializers are as follows.
The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression.
If the initializer is not a single (possibly parenthesized) expression, the source type is not defined. [...]
17.6 If the destination type is a (possibly cv-qualified) class type: [...]
17.6.3 Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution.
If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
Since the default constructor of S is explicit, it cannot convert from the source type to the destination type (S).
The syntax using m_a() is, on the other hand, not aggregate member initialization and does not invoke copy-initialization.
If I understand the standard correctly clang is correct.
According to [dcl.init.aggr]/8.5.1: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.
And further down in the same clause [dcl.init.aggr]/8.5.1: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
According to the rules for list initialization [over.match.list]/13.3.1.7
In copy-list-initialization, if an explicit constructor is chosen, the
initialization is ill-formed.
Suppose the following:
class foo
{
public:
foo() = default;
private:
std::string m_str = "Hello";
};
I am not able to find any documentation on cppreference.com or the C++11 draft that explains what the default constructor will do to m_str here. My natural assumption is that the compiler is smart enough to not initialize m_str in the defaulted constructor if it has already been initialized in-class, but maybe I'm wrong.
Can anyone explain the initialization behavior here? Any guarantees or is this unspecified?
This is well defined and specified in [class.ctor]/5:
[...] The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement. [...]
So the defaulted constructor performs the same action as:
foo() {}
This in turn applies the brace-or-equal initializer to each data member as appropriate (cf. [class.base.init]/8).
The form of initialization you are using for m_str is called brace-or-equal-initializer by the standard.
In the first paragraph under Intializers, the standard states:
8.5 Intializers
1 A declarator can specify an initial value for the identifier being declared. The identifier designates a variable being initialized. The process of initialization described in the remainder of 8.5 applies also to initializations specified by other syntactic contexts, such as the initialization of function parameters with argumet expressions (5.2.2) or the initialization of return values (6.6.3).
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
And in the section on class members...
9.2 Class members
5 A member can be initialized using a brace-or-equal-initializer.
And in the section on initializing bases and members...
12.6.2 Initializing bases and members
8 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 initializedas specified in 8.5
Coming to your class,
foo() = default;
is equivalent to:
foo(){}
When a default constructor with no member initializer list is used, the member data are default initialized. In the default initialization process, for members that have a brace-or-equal-initializer, that form is used to initialize them. In your case,
foo(){}
is equivalent to:
foo() : m_str("Hello") {}
Consider the following code:
class A {
public:
int i;
A() {}
};
class B {
public:
A a;
int i;
};
int main() {
B* p = new B {};
std::cout << p->i << " " << p->a.i << "\n";
}
Compiled with -std=c++11 in clang++, p->i turns out to be zero, but p->a.i doesn't. Shouldn't the whole object be zeroed as long as its class doesn't have user-provided constructor?
EDIT: Since there are some extensive discussion in the comments, I think it's better to add some excerpt from the standard here:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized.
To zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;
if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero-initialized and padding is initialized to zero bits;
if T is an array type, each element is zero-initialized;
if T is a reference type, no initialization is performed.
The second bullet of each applies here.
Clang is correct, per the C++11 standard plus relevant DRs
In the original C++11 specification, B{} would perform value-initialization, resulting in a.i being zero-initialized. This was a change in behavior compared to C++98 for cases like
B b = {};
... which were handled as aggregate initialization in C++98 but treated as value-initialization in C++11 FDIS.
However, the behavior in this case was changed by core issue 1301, which restored the C++98 behavior by mandating that aggregate initialization is used whenever an aggregate is initialized by a braced-init-list. Since this issue is considered a DR, it is treated as de facto applying to earlier revisions of the C++ standard, so a conforming C++11 compiler would be expected to perform aggregate initialization here rather than value-initialization.
Ultimately, it's a bad idea to rely on value-initialization to initialize your data members, especially for a class that has user-provided constructors.
It does indeed look like a bug (or, as pointed out in the comments, behaving according to C++03 despite specifying C++11). In C++11, value-initialisation should zero the members of a before calling its default constructor. Initialisation of B is governed by this rule of 8.5/7
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
The zero-initialisation should recursively zero-initialise a per this rule of 8.5/5
if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized
and, of course, zero-initialisation of a should set i to zero.
It is not a compiler bug, it is a bug in your code. The compiler seems to be implementing the C++03 behaviour, but this has crucially changed in C++11.
These are some relevant quotes from the C++03 and C++11 standards
In C++03:
To value-initialize an object of type T means:
— if T is a class type
(clause 9) with a user-declared constructor (12.1), then the default
constructor for T is called (and the initialization is ill-formed if T
has no accessible default constructor);
— if T is a non-union class
type without a user-declared constructor, then every non-static data
member and base-class component of T is value-initialized;
(emphasis mine)
In C++11:
To value-initialize an object of type T means:
— if T is a (possibly
cv-qualified) class type (Clause 9) with a user-provided constructor
(12.1), then the default constructor for T is called (and the
initialization is ill-formed if T has no accessible default
constructor);
— if T is a (possibly cv-qualified) non-union class type
without a user-provided constructor, then the object is
zero-initialized and, if T’s implicitly-declared default constructor
is non-trivial, that constructor is called.
and
To zero-initialize an object or reference of type T means:
— if T is a
scalar type (3.9), the object is set to the value 0 (zero), taken as
an integral constant expression, converted to T;
if T is a
(possibly cv-qualified) non-union class type, each non-static data
member and each base-class subobject is zero-initialized and padding
is initialized to zero bits;
Note: The following only applies to C++03:
Either remove A's user-provided constructor, or change it to
A() : i() {}
When you value-initialize a B here,
B* p = new B {};
it value-initializes its data members. Since A has a default constructor, the value-initialization results in a call to that. But that constructor does not explicitly initialize A::i, so it gets default-initialized, which for an int means no initialization is performed.
If you had not provided a default constructor for A, then the data member would get zero-initialized when an A is value-initialized.
Integral types are not required to be initialized to a value like that in a non-default constructor (since you have provided a constructor)
Change your constructor to A() : i(0) {}.
If I declare a class with default constructor and define a instance of this class with initializer list as below, will the default constructor be called for this definition? And why been called or not been called?
class Sample
{
// this should be any aggregate type in c++
};
int main()
{
Sample s = {0};
return 0;
}
In C++03, only aggregate classes may be initialized with curly braces, and an aggregate class may not have a user defined constructor
In C++0x, this syntax is supported for non aggregate types through initializer lists and calls the appropriate constructor (taking a std::initializer_list)
When you provide an brace enclosed initializer all the members of the class are copy-initialized from the corresponding expression of the brace enclosed initializer.
Such initialization is only valid for aggregates which cannot have user-declared constructors so the suppression of the compiler generated constructor is almost academic.
The Standard says ($8.5/14)
The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. The source type is not defined when the initializer is brace-enclosed or when it is a parenthesized list of expressions.
If the destination type is a (possibly cv-qualified) class type:
— If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
.
.
8.5.1/13
[Note: An aggregate array or an aggregate class may contain members of a class type with a user-declared constructor (12.1). Initialization of these aggregate objects is described in 12.6.1. ]
Also 12.6.1/2 says
When an aggregate (whether class or array) contains members of class type and is initialized by a brace-enclosed initializer-list (8.5.1), each such member is copy-initialized (see 8.5) by the corresponding assignment-expression. If there are fewer initializers in the initializer-list than members of the aggregate,
each member not explicitly initialized shall be value-initialized (8.5).
The corresponding constructor is called:
http://en.wikipedia.org/wiki/C%2B%2B0x#Initializer_lists
In C++ you may only initialize POD (plain old data) with ={0} (at least pre C++0x). So the default constructor will not be called because this won't compile.