Do uninitialized objects exist in C++?

Viewed 4934

If all objects have at least one constructor be it default c'tor defined by the compiler or user defined then how can objects be uninitialized.

3 Answers

It is possible to declare objects on which no initializations are performed. These objects do exist, they have an indeterminate value, and using this value is undefined behavior (there is an exception to this rule for chars).

Such object can be created by default-intialization. This is stated in the c++ standard, (§11.6 Initializers)[dlc.init]:

To default-initialize an object of type T means:

(7.1) — If T is a (possibly cv-qualified) class type (Clause 12), constructors are considered. The applicable constructors are enumerated (16.3.1.3), and the best one for the initializer () is chosen through overload resolution (16.3). The constructor thus selected is called, with an empty argument list, to initialize the object.

(7.2) — If T is an array type, each element is default-initialized.

(7.3) — Otherwise, no initialization is performed.

Nevertheless, static objects are always zero-initialized. So any built-in with dynamic or automatic storage duration may not be initialized, even if it is a suboject;

int i; //zero-initialized

struct A{
  int i;
  };

struct B
  {
  B(){};
   B(int i)
    :i{i}{}
  int i;
  int j;
  };
A a; //a.i is zero-initialized

int main()
  {
   int j;             //not initialized
   int k{};           //zero-initialized
   A b;               //b.i not initialized
   int* p = new int;  //*p not initialized
   A*   q = new A;    //q->i not initialized
   B ab;              //ab.i and ab.j not initialized
   B ab2{1};          //ab.j not initialized
   int xx[10];        //xx's element not initialized.

   int l = i;    //OK l==0;
   int m = j;    //undefined behavior (because j is not initialized)
   int n = b.i;  //undefined behavior 
   int o = *p; //undefined behavior 
   int w = q->i; //undefined behavior 
   int ex = x[0] //undefined behavior
   }

For member initialization [class.base.init] may help:

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 (12.2) and either

(9.1.1) — the constructor’s class is a union (12.3), and no other variant member of that union is designated by a mem-initializer-id or

(9.1.2) — 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 11.6;

(9.2) — otherwise, if the entity is an anonymous union or a variant member (12.3.1), no initialization is performed;

(9.3) — otherwise, the entity is default-initialized (11.6)

Members of a trivial anonymous union may also not be initialized.


Also one could ask if an object life-time could begin without any initialization, for exemple by using a reinterpret_cast. The answer is no: reinterpret_cast creating a trivially default-constructible object

The standard doesn't talk about existence of objects, however, there is a concept of lifetimes of objects.

Specifically, from [basic.life]

The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and

  • if the object has non-vacuous initialization, its initialization is complete

With non-vacuous initialization defined as

An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor.

We can conclude that for objects with vacuous initializations (such as ints), their lifetimes begins as soon as their storage is acquired, even if they are left uninitialized.

void foo()
{
    int i;  // i's lifetime begins after this line, but i is uninitialized
    // ...
}

† Links are added for ease of reading, they don't appear in the standard

Use byte array:

alignas(alignof(Mat4)) uint8_t result[sizeof(Mat4)];
// ..
node->updateMatrix( ..., /*result*/ reinterprect_cast<Mat4*>(&result[0])); 

The constructors of Mat4 will not trigger.

Related