In the following example:
#include <iostream>
struct A {
int z;
A(std::string) {}
A() = default;
};
int main() {
char buf[1000];
std::fill(buf, buf + 1000, 'x');
auto a = new(buf) A{};
std::cerr << "A.z: " << a->z << std::endl;
}
Compiled with GCC 4.8 outputs zero (same behavior with Clang 3.4). This seems to indicate that a is being zero-initialized before the default constructor is called.
But according to value-initialization rules on cppreference.com, the object should not be initialized before the default constructor invocation. Class A qualifies for bullet point #1 under C++11:
1) If T is a class type with at least one user-provided constructor of any kind, the default constructor is called.
Another perhaps useful data point is that if we replace A() = default with A() {} in the above example, no zero-initialization happens as expected. This seems to indicate that bullet point #2 of value initialization is being followed in the initial example which would be wrong:
2) If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
The wording that you cite for value initialization in C++11 was considered defective, see Core Working Group DR 1368 and the resolution in Core Working Group DR 1301 that changed the wording (showing additions with bold and deletions with strike through):
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9 [class]) with either no default constructor (12.1 [class.ctor]) or a default constructor that is user-provided or deleted constructor (12.1 [class.ctor]), then the object is default-initialized 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 or deleted default constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is T has a non-trivial default constructor, that constructor is called. default-initialized;
Some compilers - I believe GCC 4.8 and clang since 3.3ish - have already implemented the resolution of DR 1301.
Related
Per cppreference, the syntax for value initialization is:
[..]
T object {}; (since C++11)
[..]
It's already known that value-initialization is performed when an object is constructed with an empty initializer.
Per, [dcl.init]/8 (emphasis mine)
To value-initialize an object of type T means:
(8.1) if T is a (possibly cv-qualified) class type ([class]), then
(8.1.1) 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;
(8.1.2) otherwise, the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T
has a non-trivial default constructor, the object is
default-initialized;
(8.2) [..]
(8.3) [..]
I interpret the term "no default constructor" as that there's no default constructor declared in the class. For example,
class S
{
long double d;
friend void f(const S&);
};
void f(const S& s) { std::cout << s.d; }
int main()
{
S s{ };
f(s); // 0
}
Since the class has "no default constructor", I'm expecting that the object s is default-initialized and the member s.d has indeterminate value. Why that's not the case?
I also have a confusion in understanding the point (8.1.1). How I can write this line of code T object {} without having a default constructor or with having a deleted default constructor? Notice the bold part, It's said that, "if T has either no default constructor or a default constructor that is deleted .."
Are there situations where objects of class types are value-initialized with deleted default constructor or without default constructor at all? Am I misreading 8.1.1?
Your class S does have a default constructor; it is just defined implicitly.
Per [class.default.ctor]/1:
A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted ([dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class.
Therefore S{} will zero-initialize the object per [dcl.init]/8.1.2.
When [dcl.init]/8.1.1 refers to classes with no default constructor it means classes where the implicit default constructor doesn't get generated because user-defined constructors exist. That is, (8.1.1) would apply to the following classes:
struct NoDefaultCtor
{
NoDefaultCtor(int) {}
};
struct UserProvidedDefaultCtor
{
UserProvidedDefaultCtor() {}
};
struct DeletedDefaultCtor
{
DeletedDefaultCtor() = delete;
};
For all three of those classes, value-initialization will perform default-initialization. In the case of NoDefaultCtor and DeletedDefaultCtor that default-initialization will fail, of course, but it's important that (8.1.1) catches those types so they don't fall through and get zero-initialized by (8.1.2).
I was wondering under which circumstances a class may have no default constructor but will still be value-initialized. The typical case for "no default ctor" is that a parametrized one is present and the default ctor is not defaulted (= default).
Quoting from the 2020 standard, chapter 9.4.1, Initializers/General (the latest draft contains equivalent wording):
To value-initialize an object of type T means:
(8.1) — if T is a (possibly cv-qualified) class type (Clause 11), then
(8.1.1) — if T has either no default constructor (11.4.5.2) or a default constructor that is user-provided or deleted, then the object is default-initialized;
And
11.4.5.2 Default constructors [class.default.ctor]:
1 A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.5)
I read this such that 8.1.1 does not refer to cases with no constructor at all (because, as 11.4.5.2 explains, a default constructor is then implicitly declared). Instead I understand it as "No default constructor at all" (including implicit ones).
Value-initialization happens with new()or brace-initialization, and for excess elements in arrays that are brace-initialized (as in struct T{}; T arr[1]{};).
Neither construct compiles when there is "no default constructor". Are there situations where objects of types without default constructor are value-initialized at all? Am I misreading 8.1.1?
It’s just saying that value-initializing an object of a class type that lacks a default constructor tries and fails to default-initialize it. Note that the alternative in the next bullet still default-initializes it after zero-initializing it, so it’s not default-initialized any more for not having a default constructor.
Depending on one’s interpretation of [class.default.ctor], it might also be considered to cover the case where a class can be default-initialized via a variadic constructor template:
struct A {
template<class ...TT> A(TT...);
};
auto a=A(); // value-initialization -> default-initialization
If I try to compile this code
struct A {
const int j;
};
A a;
I'll get an expected error:
error: uninitialized const member in ‘struct A’
but, if I try to compile this one:
struct A {
const int j;
};
A * a = new A();
I'll get a successful build.
The question is: why does new allocation allows creating a variable with const member without explicit constructor and stack allocation - doesn't ?
It's not because of the heap allocation, but because of the parenthesis you use when allocating. If you do e.g.
A* a = new A;
it would also fail.
The reason it works when you add the parenthesis is because then your structure is value initialized, and for a POD-type like A value-initialization value-initializes each member, and the default value-initialization for int is zero.
That means it would probably work creating the variable on the stack as well, if you just add the value-initialization parenthesis:
A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse
Though that brings other potential problems, better use uniform initialization if you can (requies C++11):
A a{};
This is ill-formed as of C++14. §12.1 [class.ctor] says that
4 A defaulted default constructor for class X is defined as deleted
if:
[...]
any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a
user-provided default constructor,
[...]
A default constructor is trivial if it is not user-provided and if:
its class has no virtual functions (10.3) and no virtual base classes (10.1), and
no non-static data member of its class has a brace-or-equal-initializer, and
all the direct base classes of its class have trivial default constructors, and
for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default
constructor.
§8.5 [dcl.init] says in turn that
7 To default-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is
ill-formed if T has no default constructor or overload resolution
(13.3) results in an ambiguity or in a function that is deleted or
inaccessible from the context of the initialization);
[...]
8 To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is
user-provided or deleted, then the object is default-initialized;
[...]
The empty pair of parentheses results in value-initialization. The default constructor for A is defined as deleted per [class.ctor]/p4. Thus, by [dcl.init]/p8, value-initialization means default-initialization, and by p7 the initialization is ill-formed because the constructor is deleted.
The C++11 version actually allowed value-initialization in this context; it says for value-initialization that, among other things,
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.
Since by the definition above the default constructor is trivial (even though it is deleted), it is not actually called. This was considered a defect in the standard and the relevant wording was changed by CWG issue 1301.
Compiler vendors usually do implement defect resolutions, so this could be considered a bug in GCC 4.8 that's been fixed in 4.9.
On GCC 4.6.1, when I declare an instance of my own type that has a defaulted constructor, and if I instantiate an object of that type and initialize it with braces ( like Foo my_foo{}; ), the POD members in that class will only zero-initialize if there is no other constructor declared. If there is no other constructor except the defaulted one, they will zero-init like expected.
But, on GCC 4.7.3, the zero-initialize happens either way, which is the behavior I expected.
What's the difference here? Is this a compiler bug? Both of these GCC versions support defaulted constructors of the C++11 standard.
There's no real need to stick with old GCC versions, but I'd like to understand what's going on here.
note: I'm defaulting the main ctor, op=. and copy ctor simply to keep the type usable with variadic functions (clang demands this to classify the class as POD, although gcc let's me get away with using the type with variadic functions even with user-defined main ctor. bonus points if you can tell me why.)
Here is an example program to illustrate, including some output at the bottom (from binaries compiled with both GCC versions):
#include <cstdio>
// pod and pod_wctor are identical except that pod_wctor defines another ctor
struct pod {
pod( void ) = default;
pod( const pod& other ) = default;
pod& operator=( const pod& other ) = default;
int x,y,z;
};
struct pod_wctor {
pod_wctor( void ) = default;
pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { }
pod_wctor( const pod_wctor& other ) = default;
pod_wctor& operator=( const pod_wctor& other ) = default;
int x,y,z;
};
int main ( void ) {
printf("the following shuold be uninitialized:\n");
pod pee;
printf( " %i,%i,%i\n", pee.x, pee.y, pee.z);
pod_wctor podtor;
printf( " %i,%i,%i\n", podtor.x, podtor.y, podtor.z);
printf("the following shuold be initialized to 0,0,0:\n");
pod peenit{};
printf( " %i,%i,%i\n", peenit.x, peenit.y, peenit.z );
pod_wctor podtornit{};
printf( " %i,%i,%i\n", podtornit.x, podtornit.y, podtornit.z );
return 0;
}
// compiled with: g++ m.cpp -std=gnu++0x
// g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 (i386)
/****************** output *******************
the following shuold be uninitialized:
10381592,134513249,134520820
134513969,134513504,0
the following shuold be initialized to 0,0,0:
0,0,0
7367877,134513945,8724468
*********************************************/
// compiled with: g++ m.cpp -std=gnu++0x
// gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu4) (i386)
/****************** output *******************
the following shuold be uninitialized:
-1218358300,-1217268232,134520832
134514450,1,-1079827548
the following shuold be initialized to 0,0,0:
0,0,0
0,0,0
*********************************************/
By adding the constructor pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { } to your class, it loses its status as an aggregate: [dcl.init.aggregate]/1
An aggregate is an array or a class (Clause 9) with no user-provided constructors
It still is a POD, because a trivial class only needs to have no non-trivial default ctors: [class]/6
A trivial class is a class that has a default constructor (12.1), has no non-trivial default constructors,
and is trivially copyable.
The interesting point here is that for an aggregate, the list-initialization pod peenit{}; performs 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.
(Note: this is the revised order. AFAIK, in the Standard itself, the order of those two points is reversed, which must be a defect, as every aggregate has a default ctor -- the implicitly declared&defined one.)
Aggregate-initialization leads to value-initialization of the int members: [dcl.init.aggr]/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 an empty initializer list
and [dcl.init.list]/3 "Otherwise, if the initializer list has no elements, the object is value-initialized"
However, for the non-aggregate pod_wctor, the list-initialization pod_wctor podtornit{} directly performs value-initialization, which calls the default ctor. [class.ctor]/6 specifies:
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.
and in [class.base.init]/8, we find:
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
[...]
otherwise, the entity is default-initialized (8.5).
The default ctor itself does not guarantee zeroing of the members because it only does default-initialization of the members.
The difference between default- and value-initialization: [dcl.init]
[7] To default-initialize an object of type T means:
if T is a (possibly cv-qualified) class type, the default constructor for T is called [...]
[...]
otherwise, no initialization is performed.
[...]
[8] To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type with either no default constructor or a default constructor that is user-provided or deleted, then the object is default-initialized;
if T is a (possibly cv-qualified) non-union class type without a user-provided or deleted default constructor, then the object is zero-initialized and, if T has a non-trivial default constructor, default-initialized;
[...]
otherwise, the object is zero-initialized.
(I admit this confused me, and I had to revise my answer.)
pod_wctor has a default-constructor that is not user-provided. Therefore, for the list-initialization pod_wctor podtornit{}, the second bullet of value-initialization applies. The object podtornit itself is zero-initialized, which leads to a zero-initialization of its members. Only then will it be default-initialized, and the default ctor will be called. The latter does nothing, but the former guarantees the members will be zeroed.
According to N2628 related to c++0x, non-static data member initializers can be overridden by explicitly defined constructors, but it appears to be slightly nebulous about the implicitly defined copy constructor.
In particular, I've noticed that with Apple clang version 3.0, the behavior varies depending on whether the struct (or class) is a POD.
The following program returns output "1", which indicates that the copy-constructor is ignoring the right-hand-side, and instead substituting the new non-static data member initializer (in this example, the boolean true value for X::a).
#include <iostream>
#include <string>
struct X
{
std::string string1;
bool a = true;
};
int main(int argc, char *argv[])
{
X x;
x.a = false;
X y(x);
std::cout << y.a << std::endl;
}
However, confusingly, if you comment out string1:
// std::string string1;
then the behavior works as I expected (the output is "0"), presumably because there is no implicitly generated copy-constructor, and therefore the data is copied.
Does the C++0x specification really suggest that it is a good idea to allow the implicitly defined copy-constructor to not copy the contents of the right-hand side? Isn't that less useful and unintuitive? I find the non-static member initializer functionality to be quite convenient, but if this is the correct behavior, then I will explicitly avoid the feature due to its tricky and unintuitive behavior.
Please tell me I'm wrong?
UPDATE: This bug has been fixed in the Clang source repository. See this revision.
UPDATE: This bug appears fixed in Apple clang version 3.1 (tags/Apple/clang-318.0.45) (based on LLVM 3.1svn). This version of clang was distributed as part of Xcode 4.3 for Lion.
It isn't shadowy after all, see highlighted parts of the standards excerpt:
The section on defaulted copy/move constructors (§ 12.8) is a bit too lengthy to quote in it's entirety. The low-down is that non-static member fields with initializers are still simply copied by the defaulted copy/move constructor
§ 12.8:
-6. The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ] The order of initialization is the same as the order of initialization
of bases and members in a user-defined constructor (see 12.6.2). Let x be either the parameter of the
constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data
member is copied/moved in the manner appropriate to its type:
if the member is an array, each element is direct-initialized with the corresponding subobject of x;
if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
otherwise, the base or member is direct-initialized with the corresponding base or member of x.
Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor
This is the sample referred to:
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-equalinitializer will not take place. —end example ]
For completeness, the corresponding section on the defaulted default constructor:
§ 12.1
-6. A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) 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 (12.6.2) and an empty
compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed.
If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5),
the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a
class is implicitly defined, all the non-user-provided default constructors for its base classes and its nonstatic
data members shall have been implicitly defined. [ Note: An implicitly-declared default constructor
has an exception-specification (15.4). An explicitly-defaulted definition might have an implicit exception-specification,
see 8.4. —end note ]