Question

I have a function that returns default constructed value of template type:

template<typename T>
T do_stuff()
{
    return T();
}

I use it like this:

int main(int argc, char** argv)
{
    std::string str("hello");
    int a = 10;
    int *p = &a;

    str = do_stuff<std::string>();
    a = do_stuff<int>();
    p = do_stuff<int*>();

    return 0;
}

After I use it I have: str is an empty string, a eqauls 0 and p is a null pointer. I can understand why std::string variable becomes an empty string (it has default constructor that constructs an empty string). But why an int variable becomes 0 and a pointer becomes a null pointer. Is it default template behaviour?

I use gcc 4.6.6 under Linux Centos.

Was it helpful?

Solution

From this reference on value initialization:

  • If T is a class type with at least one user-provided constructor of any kind, the default constructor is called.

  • 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)

  • If T is an array type, each element of the array is value-initialized

  • Otherwise, the object is zero-initialized.

What is happening is that the last point in the above list.

OTHER TIPS

The key is in

//------vv
return T();

For example, you can test the following, which is equivalent:

int x =  int();
std::cout << x;

x will always be 0 in this case. The same is applied for the pointer - it's zero-initialized, "making" it NULL.

This is value initialization, "caused" by the parenthesis.

Because T() is value initialization, which for int and pointer types (and other basic types) is zero-initialization.

Just like to value initialize an int you'd do:

int x{};
int x = int();

//not initialized:
int x;   //if not at namespace scope
int x(); //method declaration

This is as defined in c++ 11 standard, section 8.5:

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;

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

otherwise, the object is zero-initialized

And:

To default-initialize an object of type T means:

— if T is a non-POD 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, the object is zero-initialized.

And:

To zero-initialize an object of type T means:

— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;

— if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;

— if T is a union type, the object’s first named data member 89) is zero-initialized;

— if T is an array type, each element is zero-initialized; — if T is a reference type, no initialization is performed.

That is correct behaviour for the respective types, everywhere.

Primitive types are initialized differently depending on whether you explicitly request the default value (value initialization) or don't mention the initialization (default initialization).

If you construct primitive type without mentioning initialization (this is called default initialization), the value is random:

int x;
struct Y {
    int x;
} x;
int *x = new int;

are all default initializations and will contain random value.

But if you mention the initializer, it becomes value initialization and the value is initialized to appropriate "zero" (0 for numbers, 0/nullptr for pointers):

int x = 0;
int x = int();
struct Y {
    int x;
    Y() : x() {} // the x() is important
} y;
struct Z {
    int x;
} z = {};
int *x = new int();

are all value initializations and C++11 adds the following forms

int x{};
struct Y {
    int x;
    Y() : x{} {}
} y;
struct Z {
    int x;
} z{};
int *x = new int{};

Beware of

int x(); // function, NOT A VARIABLE

it declares a function taking no arguments and returning int. This is called the "most vexing parse".

The T() is value initialization.

Use:

int x{};
int x = int();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top