Visual Studio的C4345警告是错误的吗?

Xeo*_*Xeo 20 c++ initialization visual-c++ c++11

以下代码在标记的行上触发C4345:

#include <array>
#include <iostream>

int main(){
    static unsigned const buf_size = 5;
    typedef std::array<char, buf_size> buf_type;

    char buf[] = { 5, 5, 5, 5, 5 };
    void* p = &buf[0];
    buf_type* pbuf = new (p) buf_type(); // <=== #10

    for(unsigned i=0; i < buf_size; ++i)
        std::cout << (char)((*pbuf)[i] + 0x30) << ' ';
}
Run Code Online (Sandbox Code Playgroud)

main.cpp(10):警告C4345:行为改变:使用form()的初始化程序构造的POD类型的对象将被默认初始化

因此,根据他们的警告,第10行应该具有与写入时相同的行为

buf_type* pbuf = new (p) buf_type; // note the missing '()'
Run Code Online (Sandbox Code Playgroud)

但是,输出不同.即,第一个版本将打印五秒0,而第二个版本将打印五秒5.因此,第一个版本确实是值初始化的(并且底层缓冲区零初始化),即使MSVC说它不会.

这可以被视为MSVC中的错误吗?或者我误解了警告/我的测试代码是否有问题?

Ben*_*igt 13

TL; DR版本:MSVC的行为实际上是正确的,虽然警告不正确(应该说值初始化).


因为new (p) buf_type;,MSVC执行默认初始化是正确的,因为标准(5.3.4 [expr.new])要求:

新表达式创建类型的对象T如下初始化该对象:

  • 如果省略new-initializer,则默认初始化对象(8.5); 如果没有执行初始化,则该对象具有不确定的值.
  • 否则,根据8.5的初始化规则解释new-initializer以进行直接初始化.

std::array是一种类型.对于类类型(8.5 [dcl.init]):

默认初始化的类型的对象T是指:

  • 如果T是(可能是cv限定的)类类型,T则调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);

default-initialization只为原始类型(及其原始数组)保留内存

另一方面,std::array有一个默认的默认构造函数,因此成员本身应该默认初始化.事实上,你观察到了这一点.


然后根据同一部分,该new (p) buf_type();版本导致直接初始化.

std::array是一个聚合,所以我认为这条规则(8.5.1 [dcl.init.aggr])适用:

如果列表中的initializer-clause少于聚合中的成员,则未显式初始化的每个成员都应从空的初始化列表(8.5.4)初始化.

这意味着所有元素的值初始化.

不,这是规则(8.5 [dcl.init]):

初始化器为空的括号集的对象,即(),应进行值初始化.

聚合的值初始化意味着所有元素的值初始化,因为元素是原始的,这意味着零填充.

所以MSVC的行为实际上是正确的,虽然警告是不正确的(它应该说值初始化).

已有报道,请参阅