VS2013默认初始化与值初始化的区别

10
考虑下面的代码。
struct B
{
    B() : member{}{};
    int member[10];
};

int main()
{
    B b;
}

VS2013编译器会发出以下警告:

警告 C4351:新行为:数组 'B::member' 的元素将进行默认初始化 1> test.vcxproj -> C:\Users\asaxena2\documents\visual studio 2013\Projects\test\Debug\test.exe

这里有文档here

使用C++11,并应用“默认初始化”的概念,意味着B.member的元素不会被初始化。

但我认为member{}应该执行值初始化而不是默认初始化。VS2013编译器有问题吗?

$8.5/6

默认初始化一个类型为T的对象意味着: - 如果T是一个(可能带有cv限定符的)类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则此初始化是不合法的); - 如果T是数组类型,则每个元素都会被默认初始化; - 否则,不执行任何初始化。 如果程序要求对const限定的类型T进行默认初始化,则T必须是具有用户提供的默认构造函数的类类型。

$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 ]


这是一个很好的问题,但“Visual Studio编译器行为”是一个可怕的标题。考虑将其更改为更有意义的内容。话虽如此,您是否已经验证了编译器的行为?它很可能只是一个误导性的警告。 - user743382
@hvd:已更改。谢谢。 - user3701522
3个回答

8

这似乎是一条措辞不当的警告信息(而且我很惊讶它首先就打印出了一条警告信息),但行为是正确的B::member正在进行值初始化,对于一个int数组来说,这会转换为零初始化。 可以使用以下内容进行演示:

#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;
}

如果您在Debug模式下编译并运行,就会得到以下输出结果:
0 0 0 0 0 0 0 0 0 0
-858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460

第二行中的数字是0xCCCCCCCC,这是VC++编译器在Debug模式下填充内存的调试模式。因此,B::member被进行了零初始化,而C::member没有进行初始化。
免责声明:我知道从未初始化的变量中读取是未定义的行为,但这是我能想到的最好的证明。

1
我还检查了反汇编代码,发现对于成员变量大小较大的情况,存在对memset的调用。因此,有一个显式的memset调用,这意味着它按照标准要求进行了零初始化。对于较小的n值,它会进行优化,并以不同的方式(逐个元素)设置为0。 - user3701522

2

编译器警告不正确,实际上它正在按照标准所要求的进行值初始化。

示例:

#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'
}

0

MSDN页面上说:

C4351表示您应该检查您的代码……如果您想要新行为,因为数组已明确添加到构造函数的成员初始化列表中,请使用warning pragma禁用警告。对于大多数用户来说,新行为应该是可以接受的。

因此,您需要添加 #pragma warning (suppress:4351) 行或者 #pragma warning (disable:4351) 整个文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接