9

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 ]

Praetorian
  • 100,267
  • 15
  • 224
  • 307
user3701522
  • 297
  • 3
  • 10
  • This is a good question, but "Visual Studio compiler behavior" is a horrible title. Consider changing it to something more meaningful. That said, have you verified the compiler's behaviour? It could well just be a misleading warning. –  Jun 09 '14 at 14:11
  • @hvd: changed it. Thanks – user3701522 Jun 09 '14 at 14:13

3 Answers3

7

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.

Praetorian
  • 100,267
  • 15
  • 224
  • 307
  • 1
    I also checked the disassembly and I see the call to memset for larger values of the size of the member variable. So, there is an explicit call to memset which means it is zero initializing as required by the standard. For smaller values of n it is optimizing and setting 0 in a different way (element by element) – user3701522 Jun 09 '14 at 14:23
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'
}
ecatmur
  • 137,771
  • 23
  • 263
  • 343
0

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.

parsley72
  • 6,932
  • 8
  • 54
  • 78