【问题标题】:VS2013 default initialization vs value initializationVS2013 默认初始化 vs 值初始化
【发布时间】:2014-06-09 14:05:31
【问题描述】:

考虑下面的代码

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

T 类型的对象或引用的列表初始化定义如下:
— 如果初始化列表没有元素并且T 是具有默认构造函数的类类型,则该对象是值初始化的。
— 否则,如果 T 是聚合,则执行聚合初始化 (8.5.1)。

如果列表中的初始化子句少于聚合中的成员,则每个未显式初始化的成员都应从空的初始化器列表中初始化(8.5.4)。 [ 例子:

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

1初始化ss.a,用"asdf"初始化ss.b,用int()形式的表达式的值初始化ss.c,即0——结束示例]

【问题讨论】:

  • 这是一个很好的问题,但是“Visual Studio 编译器行为”是一个可怕的标题。考虑将其更改为更有意义的内容。也就是说,您是否验证了编译器的行为?这很可能只是一个误导性警告。
  • @hvd:改变了它。谢谢

标签: c++ c++11 visual-studio-2013 constructor value-initialization


【解决方案1】:

这似乎是一条措辞不正确的警告消息(我很惊讶它首先打印了一条警告),但行为是正确的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++ 编译器在调试模式下填充内存的调试模式。因此B::member 被零初始化,而C::member 没有初始化。

免责声明:我知道从未初始化的变量中读取是未定义的行为,但这是我能想到的最好的证明

【讨论】:

  • 我还检查了反汇编,我看到调用 memset 以获得更大的成员变量大小值。因此,有一个对 memset 的显式调用,这意味着它按照标准的要求进行零初始化。对于较小的 n 值,它以不同的方式优化和设置 0(逐个元素)
【解决方案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'
}

【讨论】:

    【解决方案3】:

    MSDN page 说:

    C4351 意味着你应该检查你的代码...如果你想要新的 行为,这很可能是因为数组被显式添加到 构造函数的成员初始化列表,使用warning pragma 禁用警告。新行为对大多数人来说应该没问题 用户。

    所以你必须为一行添加#pragma warning (suppress:4351),或者为整个文件添加#pragma warning (disable:4351)

    【讨论】:

      猜你喜欢
      • 2014-12-29
      • 2020-02-08
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-29
      • 2015-03-22
      相关资源
      最近更新 更多