VS2013 default initialization vs value initializat

2019-04-22 04:54发布

问题:

Consider the code below

struct B
{
    B() : member{}{};
    int member[10];
};

int main()
{
    B b;
}

VS2013 compiler gives the following warning:

warning C4351: new behavior: elements of array 'B::member' will be default initialized 1> test.vcxproj -> C:\Users\asaxena2\documents\visual studio 2013\Projects\test\Debug\test.exe

This is documented here

With C++11, and applying the concept of 'default initialization', means that elements of B.member will not be initialized.

But I believe that member{} should perform value initialization and not default initialization. Is the VS2013 compiler broken?

$8.5/6

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.
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

$8.5.1

List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).

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

  struct S { int a; const char* b; int c; };
  S ss = { 1, "asdf" };

initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0. —end example ]

回答1:

It seems to be an incorrectly worded warning message (and I'm surprised it is printing a warning in the first place), but the behavior is correct. B::member is being value initialized, which for an array of int turns into zero initialization. This can be demonstrated using the following:

#include <iostream>

struct B
{
    B() : member{}{};
    int member[10];
};

struct C
{
    C() {};
    int member[10];
};

int main()
{
    B b;
    for(auto const& a : b.member) std::cout << a << ' ';
    std::cout << std::endl;

    C c;
    for(auto const& a : c.member) std::cout << a << ' ';
    std::cout << std::endl;
}

If you compile and run in Debug mode this results in the output:

0 0 0 0 0 0 0 0 0 0
-858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460

The numbers in the second line are 0xCCCCCCCC, the debug pattern the VC++ compiler fills memory with in Debug mode. Thus B::member is being zero-initialized, while no initialization is performed for C::member.

Disclaimer: I know that reading from an uninitialized variable is undefined behavior, but this is the best proof I could come up with.



回答2:

The compiler warning is incorrect; it is actually performing value-initialization as required by the standard.

Example:

#include <iostream>

struct B {
    B() : member{}{};
    int member[10];
};

int main() {
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    B &b = *new (a) B;
    std::cout << b.member[9];  // prints '0'
}


回答3:

The MSDN page says:

C4351 means that you should inspect your code... If you want the new behavior, which is likely, because the array was explicitly added to the constructor's member initialization list, use the warning pragma to disable the warning. The new behavior should be fine for most users.

So you have to add #pragma warning (suppress:4351) for one line or #pragma warning (disable:4351) for the whole file.