Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

defaulted ctor differences between gcc 4.6 and 4.7

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
*********************************************/
like image 365
Brandon Avatar asked Sep 19 '13 17:09

Brandon


1 Answers

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.

like image 193
dyp Avatar answered Sep 22 '22 19:09

dyp