Value Initialization and Non Pod Types

Value initialization and Non POD types

Visual Studio has known bugs in all current versions (2005, 2008, 2010) where it doesn't correctly implement value-initialization for non-POD types that don't have a user declared constructor.

By the language rules none of you asserts should fire but do exhibit the compiler issues. These are some of the bug reports, note that they are all closed or resolved as "Won't Fix".

http://connect.microsoft.com/VisualStudio/feedback/details/564268/c-value-initialization

http://connect.microsoft.com/VisualStudio/feedback/details/484295/vc-does-not-value-initialize-members-of-derived-classes-without-user-declared-constructor

http://connect.microsoft.com/VisualStudio/feedback/details/100744/value-initialization-in-new-expression

Default initialization of POD vs. non-POD class types

From 8.5.9 of the 2003 standard:

If no initializer is specified for an object, and the object is of
(possibly cv-qualified) non-POD class type (or array thereof), the
object shall be default-initialized; if the object is of
const-qualified type, the underlying class type shall have a
user-declared default constructor. Otherwise, if no initializer is
specified for a nonstatic object, the object and its subobjects, if
any, have an indeterminate initial value
); if the object or any of
its subobjects are of const-qualified type, the program is ill-formed.

The class you show is a POD, so the highlighted part applies, and your object will not be initialized at all (so section 8.5/5, which you quote, does not apply at all).

Edit: As per your comment, here the quote from section 8.5/5 of the final working draft of the current standard (I don't have the real standard, but the FDIS is supposedly very close):

To default-initialize an object of type T means:

— if T is a (possibly
cv-qualified) class type (Clause 9), the default constructor for T is
called (and the initialization is ill-formed if T has no accessible
default constructor);

— if T is an array type, each element is
default-initialized;

otherwise, no initialization is performed.

Default initialization of POD types in C++

Local variables with automatic storage duration are not being initialized automatically. Since using uninitialized variables produces undefined behavior, it is a good practice to explicitly initialize your variables even when it's redundant.

About POD types that are being zero-initialized, C++03 standard 3.6.2 Initialization of non-local objects states:

§1 Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

So it's guaranteed by standard that POD types with static storage duration (whatever their scope is) will be zero-initialized.

POD members of a class (without explicit initialization in a constructor)

This situation is described in 12.6.2 Initializing bases and members, that states (selected parts):

If a given nonstatic data member or base class is not named 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 nonstatic data member..., and the entity class is a non-POD class, the entity is default-initialized (8.5)...

Otherwise, the entity is not initialized...

After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor’s mem-initializers, nor default-initialized, nor value-initialized, nor given a value during execution of the body of the constructor, the member has indeterminate value.

Example:

class C
{
public:
C(int x, int z) : x(x), z(z) { }
int x, y, z;
};

int main(void)
{
C* c = new C(1,3);
std::cout << c->y; // value of y is undetermined !!!
}

Zero-initialization of POD types

As written, this is aggregate initialization. The applicable rule is (§8.5.1 [dcl.init.aggr]/p7):

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 (8.5.4).

The relevant parts of §8.5.4 [dcl.init.list]/p3 is:

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.
  • [irrelevant items omitted]
  • Otherwise, if the initializer list has no elements, the object is value-initialized.

In short, sub-aggregates are recursively aggregate-initialized from an empty initializer list. Everything else is value-initialized. So the end result is everything being value-initialized, with everything being a POD, value-initialization means zero-initialization.


If T is POD but not an aggregate, then aggregate initialization doesn't apply, so you hit the second bullet point in §8.5.4 [dcl.init.list]/p3, which results in value-initialization of the entire object instead. POD classes must have a trivial (and so not-user-provided) default constructor, so value-initialization for them means zero-initialization as well.

What is instantiated when a POD structure is declared without using a default constructor?

default-initialize an object of type T means:

... otherwise, the object is zero-initialized.

No.

Your first linked answer is correct about C++11 onwards, and this is called default-initialization. It's different to default-construction or value initialization.

The second linked answer was probably correct for C++03, but is wrong for C++11 onwards (even though it was written in 2012). I don't have a copy of the '03 standard to verify, and it was a long time ago.

The effects of default initialization are:

  • if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;

  • if T is an array type, every element of the array is default-initialized;

  • otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.


Local copy of N4659 agrees with the summary above:

11.6 Initializers [dcl.init]

...

(7.3) Otherwise, no initialization is performed

The section on new-expressions even refers to 11.6 and then says

Note: If no initialization is performed, the object has an indeterminate value.— end note


Current draft has

9.3 Initializers [dcl.init]

...

(7.3) Otherwise, no initialization is performed.

How are non-POD static values initialized?

C++11 requires the initialization of function local static variables to be thread-safe. So at least in compilers that are compliant, there'll typically be some sort of synchronization primitive in use that'll need to be checked each time the function is entered.

For example, here's the assembly listing for the code from this program:

#include <string>
void foo() {
static std::string str("Hello, Stack Overflow!");
}

int main() {}

.LC0:
.string "Hello, Stack Overflow!"
foo():
cmpb $0, guard variable for foo()::str(%rip)
je .L14
ret
.L14:
pushq %rbx
movl guard variable for foo()::str, %edi
subq $16, %rsp
call __cxa_guard_acquire
testl %eax, %eax
jne .L15
.L1:
addq $16, %rsp
popq %rbx
ret
.L15:
leaq 15(%rsp), %rdx
movl $.LC0, %esi
movl foo()::str, %edi
call std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
movl guard variable for foo()::str, %edi
call __cxa_guard_release
movl $__dso_handle, %edx
movl foo()::str, %esi
movl std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string(), %edi
call __cxa_atexit
jmp .L1
movq %rax, %rbx
movl guard variable for foo()::str, %edi
call __cxa_guard_abort
movq %rbx, %rdi
call _Unwind_Resume
main:
xorl %eax, %eax
ret

The __cxa_guard_acquire, __cxa_guard_release etc. are guarding initialization of the static variable.

Does C++ do value initialization of a POD typedef?

It does. For a type T, T() value-initializes an "object" of type T and yields an rvalue expression.

int a = int();
assert(a == 0);

Same for pod-classes:

struct A { int a; };
assert(A().a == 0);

Also true for some non-POD classes that have no user declared constructor:

struct A { ~A() { } int a; };
assert(A().a == 0);

Since you cannot do A a() (creates a function declaration instead), boost has a class value_initialized, allowing to work around that, and C++1x will have the following, alternative, syntax

int a{};

In the dry words of the Standard, this sounds like

The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized

Since a typedef-name is a type-name, which is a simple-type-specifier itself, this works just fine.

Confused on how empty user defined constructor will initialize non-static non-POD member variables

In the C++11 standard, section 12.6 paragraph 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 initialized
    as specified in 8.5;
  • otherwise, if the entity is a variant member (9.5), no initialization is performed;
  • otherwise, the entity is default-initialized (8.5).

You are encountering the third case, where there is no initializer for the member and the member isn't a variant member, so in that case it is default-initialized.

Also, from paragraph 10:

In a non-delegating constructor, initialization proceeds in the following order:
- First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
    (regardless of the order of the mem-initializers).
  • Then, non-static data members are initialized in the order they were declared in the class definition
    (again regardless of the order of the mem-initializers).
  • Finally, the compound-statement of the constructor body is executed.

Regardless of what you specify in your constructor, the members are going to be initialized just before the body of the constructor is executed.

A mem-initializer-id is the identifier used to refer to a member in a constructor initializer list:

class Test {
public:
Test() : i() {} // Here `i` is a mem-initializer-id
Test2 i;
};


Related Topics



Leave a reply



Submit