【发布时间】:2014-05-15 23:50:17
【问题描述】:
我已经定义了一个 POD,打算将其用作不可变数据存储。为此,我将其成员限定为const,并期望对实例进行值初始化(在某些情况下进行零初始化)。考虑以下代码:
struct Foo
{
const int value;
};
int main()
{
Foo foo{ };
return 0;
}
当我尝试对此 POD 进行零初始化时,由于 Foo::value 上的 const 限定符,我在 Visual Studio (C3852) 中收到编译器错误。如果我删除限定符,代码编译得很好。
确切的错误信息是:
错误 C3852:“Foo::value”类型为“const int”:聚合初始化无法初始化此成员 const 成员不能被默认初始化,除非它们的类型有用户定义的默认构造函数
根据标准 (draft n3337),§8.5/5(零初始化):
对 T 类型的对象或引用进行零初始化意味着:
—如果 T 是标量类型 (3.9),则将对象设置为值 0(零),作为整数常量表达式, 转换为 T;
——如果 T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类 子对象初始化为零,填充初始化为零位;
——如果 T 是一个(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员被零初始化 并且填充被初始化为零位;
——如果T是数组类型,每个元素都初始化为零;
——如果 T 是引用类型,则不执行初始化。
和§8.5/6(默认初始化):
默认初始化 T 类型的对象意味着:
——如果 T 是一个(可能是 cv 限定的)类类型(第 9 条),则调用 T 的默认构造函数(并且 如果 T 没有可访问的默认构造函数,则初始化是非良构的);
——如果 T 是一个数组类型,每个元素都是默认初始化的;
——否则,不执行初始化。 如果程序要求对 const 限定类型 T 的对象进行默认初始化,则 T 应是一个类 使用用户提供的默认构造函数键入。
和§8.5/7(值初始化):
对 T 类型的对象进行值初始化意味着:
——如果 T 是一个(可能是 cv 限定的)类类型(第 9 条),带有一个用户提供的构造函数(12.1),那么 调用 T 的默认构造函数(如果 T 没有可访问的默认值,则初始化格式错误 构造函数);
——如果 T 是一个(可能是 cv 限定的)非联合类类型,没有用户提供的构造函数,那么对象 是零初始化的,如果 T 的隐式声明的默认构造函数是非平凡的,则该构造函数是 调用。
——如果T是一个数组类型,那么每个元素都是值初始化的;
——否则,对象被零初始化。
我对标准的阅读使我相信我的 POD 应该是零初始化的;未默认初始化。我是否误解了标准中描述的初始化过程?
编辑:考虑到接受的答案和相关 cmets 中提供的详细信息,这看起来像是 VS 实现中的潜在错误(即,实现可能基于标准的过时版本) .我创建了一个 Microsoft Connect 票证来跟踪它,可以在这里找到:
【问题讨论】:
-
为什么有 const 成员?为什么不只是一个 const Foo?
-
您没有使用
Foo foo{ };语法进行零初始化。这是聚合初始化,初始化器比成员少,这意味着数据成员将从空的{}初始化。 -
@BenVoigt AFAIK
{}始终是 list-init,并且至少在以后的草案 (>= n3485) 中,聚合 init 是 list-init 的第一个可能选项。不过,n3337 在 list-init 中的 aggregate-init 之前仍然有 value-init。看CWG DR 1301的分辨率 -
@Lilshieste 我猜这与 CWG1301 之前/之后有关。
-
呃,问题中的规则也改变了该草案和 C++11 之间的措辞显着。
标签: c++ c++11 language-lawyer