简体   繁体   English

C ++标准是否允许使用const成员对POD对象进行零初始化?

[英]Does the C++ standard allow zero-initialization of a POD object with const members?

I've defined a POD that I'm planning on using as an immutable data store. 我已经定义了一个POD,我计划将其用作不可变数据存储。 To accomplish this, I've qualified its members as const , and am expecting to value-initialize instances (and zero-initialize in some cases). 为了实现这一点,我将其成员限定为const ,并且期望对值初始化实例(在某些情况下为零初始化)。 Consider the following code: 请考虑以下代码:

struct Foo
{
    const int value;
};

int main()
{
    Foo foo{ };

    return 0;
}

When I try to zero-initialize this POD, I get a compiler error in Visual Studio ( C3852 ) because of the const qualifier on Foo::value . 当我尝试对此POD进行零初始化时,由于Foo::value上的const限定符,我在Visual Studio( C3852 )中遇到编译器错误。 If I remove the qualifier, the code compiles fine. 如果我删除限定符,代码编译正常。

The exact error message is: 确切的错误消息是:

error C3852: 'Foo::value' having type 'const int': aggregate initialization could not initialize this member const members cannot be default initialized unless their type has a user defined default constructor 错误C3852:'Foo :: value'类型为'const int':聚合初始化无法初始化此成员const成员不能默认初始化,除非它们的类型具有用户定义的默认构造函数

According to the standard ( draft n3337 ), §8.5/5 ( zero-initialization ): 根据标准( 草案n3337 ),§8.5/ 5( 零初始化 ):

To zero-initialize an object or reference of type T means: 零初始化T类型的对象或引用意味着:

— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T; - 如果T是标量类型(3.9),则将对象设置为值0(零),作为整数常量表达式,转换为T;

— if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits; - 如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;

— if T is a (possibly cv-qualified) union type, the object's first non-static named data member is zeroinitialized and padding is initialized to zero bits; - 如果T是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员为零初始化,并且填充初始化为零位;

— if T is an array type, each element is zero-initialized; - 如果T是数组类型,则每个元素都是零初始化的;

— if T is a reference type, no initialization is performed. - 如果T是引用类型,则不执行初始化。

and §8.5/6 ( default-initialization ): 和§8.5/ 6( 默认初始化 ):

To default-initialize an object of type T means: 默认初始化T类型的对象意味着:

— 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); - 如果T是一个(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);

— if T is an array type, each element is default-initialized; - 如果T是数组类型,则每个元素都是默认初始化的;

— 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. 如果程序要求对const限定类型T的对象进行默认初始化,则T应为具有用户提供的默认构造函数的类类型。

and §8.5/7 ( value-initialization ): 和§8.5/ 7( 值初始化 ):

To value-initialize an object of type T means: 对值类型T的对象进行值初始化意味着:

— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor); - 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);

— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is called. - 如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数。

— if T is an array type, then each element is value-initialized; - 如果T是数组类型,则每个元素都是值初始化的;

— otherwise, the object is zero-initialized. - 否则,对象被零初始化。

My reading of the standard leads me to believe that my POD should be zero-initialized; 我对标准的阅读使我相信我的POD应该是零初始化的; not default-initialized. 没有默认初始化。 Am I misunderstanding the initialization process described in the standard? 我是否误解了标准中描述的初始化过程?

EDIT: Considering the details provided in the accepted answer and related comments, this looks like a potential bug in the VS implementation (ie, the implementation may be based on an outdated version of the standard). 编辑:考虑到接受的答案和相关评论中提供的详细信息,这看起来像VS实现中的潜在错误(即,实现可能基于标准的过时版本)。 I've created a Microsoft Connect ticket to track this, which can be found here: 我已经创建了一个Microsoft Connect票据来跟踪它,可以在这里找到:

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

@dyp's comment that aggregate initialization occurs is correct, but leads to zero-initialization of the member. @dyp的注释表明聚合初始化发生是正确的,但会导致成员的零初始化。

Foo foo{ };

is list-initialization, so we start at 8.5.4. 是列表初始化,所以我们从8.5.4开始。 8.5.4p3 says that (ordering from draft n3690, which matches C++11, and still in n3797) 8.5.4p3说(从草案n3690订购,与C ++ 11匹配,仍然在n3797)

List-initialization of an object or reference of type T is defined as follows: 列表初始化对象或类型T的引用定义如下:

  • If T is an aggregate, aggregate initialization is performed (8.5.1). 如果T是聚合,则执行聚合初始化(8.5.1)。
  • Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized. 否则,如果初始化程序列表没有元素且T是具有默认构造函数的类类型,则对象将进行值初始化。
  • Otherwise, if T is a specialization of std::initializer_list<E> , a prvalue initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (8.5). 否则,如果Tstd::initializer_list<E> ,则如下所述构造prvalue initializer_list对象,并根据相同类型的类初始化对象的规则初始化对象(8.5) 。
  • Otherwise, if T is a class type, constructors are considered. 否则,如果T是类类型,则考虑构造函数。 The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). 枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。 If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed. 如果转换任何参数需要缩小转换(见下文),则程序格式错误。
  • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E , the object or reference is initialized from that element; 否则,如果初始化列表具有E类型的单个元素且T不是引用类型或其引用类型与E引用相关,则从该元素初始化对象或引用; if a narrowing conversion (see below) is required to convert the element to T , the program is ill-formed. 如果需要缩小转换(见下文)将元素转换为T ,则程序格式不正确。
  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary. 否则,如果T是引用类型,则由T引用的类型的prvalue临时值是copy-list-initialized或direct-list-initialized,具体取决于引用的初始化类型,并且引用绑定到该临时值。
  • Otherwise, if the initializer list has no elements, the object is value-initialized. 否则,如果初始化列表没有元素,则对象进行值初始化。
  • Otherwise, the program is ill-formed. 否则,该计划是不正确的。

In the first case, we must visit 8.5.1 for aggregate initialization of the class. 在第一种情况下,我们必须访问8.5.1以进行类的聚合初始化。 p7: P7:

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 its brace-or-equal-initializer or, if there is no brace-or-equal-initializer, from an empty initializer list (8.5.4). 如果列表中的initializer-clause少于聚合中的成员,那么未明确初始化的每个成员都应从其brace-or-equal-initializer初始化,或者,如果没有brace-or-equal-initializer,从空的初始化列表(8.5.4)。

So the value member gets list-initialized from an empty initializer list, which recurses into the above rule, reaching the penultimate case, that of value initialization. 因此, value成员从空的初始化列表中进行列表初始化,该列表将递归到上述规则,达到倒数第二种情况,即值初始化。 And of course from the rules in the question, that is zero-initialization. 当然,从问题中的规则来看,这是零初始化。


dyp mentions that prior to n3485, which implemented CWG 1301 , the rule for default construction took precedence, and would try and fail to access the deleted default constructor. dyp提到在实现CWG 1301的 n3485之前,默认构造的规则优先,并且会尝试并且无法访问已删除的默认构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM