Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused by the difference about value-initializtion between C++ 11&14 [duplicate]

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.

like image 931
goodbyeera Avatar asked Feb 21 '14 11:02

goodbyeera


People also ask

What is difference between initialization and declaration in C?

Declaration tells the compiler about the existence of an entity in the program and its location. When you declare a variable, you should also initialize it. Initialization is the process of assigning a value to the Variable. Every programming language has its own method of initializing the variable.

What is the difference between C++ and C ++ 11?

The C++11 Standard Library was also revamped with new algorithms, new container classes, atomic operations, type traits, regular expressions, new smart pointers, async() facility, and of course a multithreading library.

How many types of initialization are there in C?

There are two ways to initialize the variable. One is static initialization in which the variable is assigned a value in the program and another is dynamic initialization in which the variables is assigned a value at the run time.

What's the difference between initializing a variable and assigning a value to a variable?

Initialization gives a variable an initial value at the point when it is created. Assignment gives a variable a value at some point after the variable is created.


3 Answers

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.

like image 116
Richard Smith Avatar answered Sep 21 '22 17:09

Richard Smith


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.

like image 38
Mike Seymour Avatar answered Sep 23 '22 17:09

Mike Seymour


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.

like image 31
juanchopanza Avatar answered Sep 22 '22 17:09

juanchopanza