首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++标准允许对带有const成员的POD对象进行零初始化吗?

C++标准允许对带有const成员的POD对象进行零初始化吗?
EN

Stack Overflow用户
提问于 2014-04-03 21:51:29
回答 1查看 1.1K关注 0票数 13

我已经定义了一个POD,我计划使用它作为一个不可变的数据存储。为此,我将其成员限定为const,并期望对实例进行值初始化(在某些情况下为零初始化)。考虑以下代码:

代码语言:javascript
复制
struct Foo
{
    const int value;
};

int main()
{
    Foo foo{ };

    return 0;
}

当我试图对这个命令进行零初始化时,由于C3852上的const限定符,我在Visual ( Foo::value )中得到了一个编译器错误。如果删除限定符,代码就会编译得很好。

确切的错误信息是:

错误C3852:'Foo::value‘具有类型'const’:聚合初始化无法初始化此成员,除非其类型具有用户定义的默认构造函数,否则不能默认初始化

根据标准(草案n3337),第8.5/5节

要将对象或T类型的引用初始化为零,意味着: -如果T是标量类型(3.9),则将对象设置为0(0)值,作为积分常量表达式,转换为T; -如果T是(可能是cv限定的)非并类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,填充被初始化为零位; -如果T是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员被零初始化,填充被初始化为零位; -如果T是数组类型,则每个元素都为零初始化; -如果T是引用类型,则不执行初始化。

和§8.5/6 (default-initialization):

要默认-初始化T类型的对象意味着: -如果T是(可能是cv限定的)类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化不正确); -如果T是数组类型,则每个元素都是默认初始化的; -否则,不执行初始化。如果程序调用const限定类型T的对象的默认初始化,则T应该是具有用户提供的默认构造函数的类类型。

和§8.5/7 (value-initialization):

要对T类型的对象进行值初始化,意味着: -如果T是用户提供的构造函数(12.1) (可能是cv限定的)类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化不正确); -如果T是(可能是cv限定的)非联合类类型,而没有用户提供的构造函数,那么对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数。 -如果T是数组类型,则每个元素都是值初始化的; -否则,对象将为零初始化。

我对标准的理解使我相信,我的POD应该是零初始化的,而不是默认初始化的。我是否误解了标准中描述的初始化过程?

编辑:考虑到在接受的答案和相关注释中提供的详细信息,这看起来像是VS实现中的一个潜在错误(也就是说,实现可能基于过时的标准版本)。我创建了一个Microsoft票据来跟踪它,在这里可以找到:

https://connect.microsoft.com/VisualStudio/feedback/details/846222/c-compiler-uses-incorrect-initialization-scheme-for-certain-objects

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-04-03 22:06:56

@dyp关于聚合初始化发生的评论是正确的,但会导致成员的零初始化。

代码语言:javascript
复制
Foo foo{ };

是列表初始化,所以我们从8.5.4开始。8.5.4p3表示(从与C++11匹配的、仍在n3797中的草案n3797中订购)

列表-对象或T类型引用的初始化定义如下:

  • 如果T 是聚合的,则执行聚合初始化(8.5.1)。
  • 否则,如果初始化程序列表没有元素,并且T是一个具有默认构造函数的类类型,则该对象是值初始化的。
  • 否则,如果Tstd::initializer_list<E>的专门化,则按照下面描述的方式构造prvalue initializer_list对象,并根据从同一类型的类(8.5)初始化对象的规则来初始化对象。
  • 否则,如果T是类类型,则考虑构造函数。列举了适用的构造函数,并通过过载解析(13.3,13.3.1.7)选择了最优的构造函数。如果需要收缩转换(见下文)来转换任何参数,则程序的格式是错误的。
  • 否则,如果初始化程序列表只有一个类型为E的元素,或者T不是引用类型,或者其引用类型与E相关,则从该元素初始化对象或引用;如果需要缩小转换(参见下文)才能将元素转换为T,则程序格式不正确。
  • 否则,如果T是引用类型,则T引用的类型的prvalue临时值是复制列表初始化或直接列表初始化,这取决于引用的初始化类型,并且引用绑定到该临时引用。
  • 否则,如果初始化程序列表中没有元素,则该对象是值初始化的.。
  • 否则的话,这个程序是不正确的.

在第一种情况下,我们必须访问8.5.1以获得类的聚合初始化。p7:

如果列表中的初始化者-子句少于聚合中的成员,则未显式初始化的每个成员都应从其大括号或等初始化项中初始化,或者如果没有大括号或等初始化项,则从空初始化程序列表(8.5.4)中初始化。

因此,value成员从空初始化器列表获得列表初始化,该列表递归到上述规则中,达到倒数第二种情况,即值初始化。当然,从问题中的规则来看,那就是零初始化。

dyp提到,在n3485实现CWG 1301之前,默认构造的规则优先,并且尝试访问已删除的默认构造函数而失败。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22849688

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档