[英]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 ofstd::initializer_list<E>
, a prvalueinitializer_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).否则,如果
T
是std::initializer_list<E>
,则如下所述构造prvalueinitializer_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 eitherT
is not a reference type or its referenced type is reference-related toE
, the object or reference is initialized from that element;否则,如果初始化列表具有
E
类型的单个元素且T
不是引用类型或其引用类型与E
引用相关,则从该元素初始化对象或引用; if a narrowing conversion (see below) is required to convert the element toT
, the program is ill-formed.如果需要缩小转换(见下文)将元素转换为
T
,则程序格式不正确。- Otherwise, if
T
is a reference type, a prvalue temporary of the type referenced byT
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.