首先,我有一个结构,其中一个值具有默认值
struct S {
int a = 1;
};
当gcc和clang都是非const / non-constexpr时,可以默认构造此类型。在这两者下,std::is_pod<S>::value
是false
。奇怪的行为如下:
S s1; // works under both
const S s2{}; // works under both
const S s3; // only works in gcc, clang wants a user-provided constructor
以下尝试都没有对clang产生影响:
struct S {
int a = 1;
constexpr S() = default; // defaulted ctor
virtual void f() { } // virtual function, not an aggregate
private:
int b = 2; // private member, really not an aggregate
};
我唯一可以做的就是明确添加constexpr S() { }
。 const S s;
const S s{};
在S{};
失败时,特别是当类型不是聚合时,似乎真的错了。
标准让我觉得Clang是对的 N4296:8.5 / 7
如果程序要求默认初始化a的对象 const限定类型T,T应为具有用户提供的类类型 默认构造函数
那么为什么gcc允许这个,并且{{1}}不是默认初始化,即使该类型不是POD或聚合?
答案 0 :(得分:16)
const S s3;
由[dcl.init] / 12:
涵盖如果没有为对象指定初始化程序,则默认初始化该对象。
因此,根据您的引用的要求,必须存在用户提供的默认构造函数。添加一个这样的
struct S {
int a = 1;
constexpr S(){}
};
然后makes the declaration compile fine。
[..]尤其是当类型不是聚合时。
S
是您的案例中的汇总,以及const S s{}
有效的原因。聚合初始化适用于const S s{}
,一切都很好
如果S
不是聚合,
对象或类型T的引用的列表初始化定义为 如下:
- 如果
T
是聚合,则执行聚合初始化(8.5.1)。- 否则,如果初始化列表没有元素且
T
是具有默认构造函数的类类型,则该对象是值初始化的。
现在考虑值初始化的定义:
value-initialize
T
类型的对象意味着:
- 如果
T
是 (可能是cv-qualified)类类型(第9条),没有默认值 构造函数(12.1)或用户提供或删除的默认构造函数,然后对象默认初始化;- 如果
T
是一个(可能是 cv-qualified)没有用户提供或删除默认值的类类型 那么构造函数 对象是零初始化的,并且检查默认初始化的语义约束,如果T
有一个非平凡的默认构造函数,该对象是默认初始化的;
默认的ctor确实非常重要,因为一个成员有一个初始化器([class.ctor] /4.9),但这是无关紧要的,因为无论如何都要检查约束。因此默认初始化它和行
const S s{};
与
一样有效(或无效!)const S t;
那为什么gcc允许这个
好:
答案 1 :(得分:5)
所以看起来gcc正基于DR 253,即使这还没有解决。我们可以从以下gcc bug report中看到这一点:
这是设计上的,因为正如DR 253所示,规范标准存在缺陷。
而gcc change that brought this into effect说:
Core 234 - 允许const对象没有初始化程序或 用户提供的默认构造函数(如果是默认构造函数) 初始化所有子对象。
技术上clang
是正确的,gcc
不符合要求,但似乎他们认为DR 253
会对他们有利。如果主要关注的是不确定的初始值,这就完全可以了,据我所知。此更改记录在gcc 4.6 release notes:
在4.6.0和4.6.1中,G ++不再允许const限定的对象 除非类型具有用户声明的类型,否则将初始化默认类型 默认构造函数。在4.6.2中,G ++实现了提议的解决方案 DR 253,如果初始化全部,则允许默认初始化 子对象。无法编译的代码可以通过提供一个来修复 初始化程序,例如
struct A { A(); }; struct B : A { int i; }; const B b = B();