常数 T{};作品,常量 T;当 T 是非 POD 时失败,

const T{}; works, const T; fails when T is a non-POD,

首先,我有一个具有默认值的结构

struct S {
    int a = 1;
};

当gcc和clang都为non-const / non-constexpr时,可以默认构造该类型。在两者下,std::is_pod<S>::valuefalse。奇怪的行为如下:

S s1; // works under both
const S s2{}; // works under both
const S s3; // only works in gcc, clang wants a user-provided constructor

None 以下尝试对 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{}; 时失败对我来说似乎真的是错误的,尤其是当类型不是聚合时。

标准让我觉得Clang是对的
N4296: 8.5/7

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

那么为什么 gcc 允许这样做,并且 S{}; 不是默认初始化,即使类型不是 POD 或聚合?

const S s3;

被[dcl.init]/12覆盖:

If no initializer is specified for an object, the object is default-initialized.

因此,根据您的报价要求,必须存在 user-provided 默认构造函数。像这样添加一个

struct S {
    int a = 1;
    constexpr S(){}
};

然后 makes the declaration compile fine.

[..] especially when the type is not an aggregate.

S 在您的情况下是一个聚合,也是 const S s{} 有效的原因。 const S s{} 应用聚合初始化,一切正常。
如果 S 不是聚合,则

List-initialization of an object or reference of type T is defined as follows:

  • If T is an aggregate, aggregate initialization is performed (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.

现在考虑值初始化的定义:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;

默认的 ctor 确实很重要,因为成员有一个初始化器 ([class.ctor]/4.9),但这无关紧要,因为无论如何都会检查约束。因此 default-initialization 是,并且行

const S s{};

一样有效(或无效!)
const S t;

So why does gcc allow this

嗯:

  1. 就目前的标准而言,GCC是不兼容的;见上。

  2. 十五年前创建的一个活跃的 CWG 问题 #253 涵盖了类似的场景。 2011 年一次会议关于此的最后说明说

    If the implicit default constructor initializes all subobjects, no initializer should be required.

    S 的隐式默认构造函数就是这种情况,这将使您的所有行都有效。

  3. GCC开发者(如here)暗示,由于委员会基本同意上述决议,GCC目前的行为是可行的,不应调整。因此,人们很可能会争辩说 GCC 是正确的并且标准被打破了。

所以看起来 gcc 是基于 DR 253 even though this is not resolved yet. We can see this from the the following gcc bug report 的,它说:

This is by design, because as DR 253 shows, the normative standard is flawed.

并且 gcc change that brought this into effect 表示:

Core 234 - allow const objects with no initializer or user-provided default constructor if the defaulted constructor initializes all the subobjects.

所以从技术上讲,clang 是正确的,而 gcc 不符合要求,但他们似乎相信 DR 253 会以对他们有利的方式得到解决。如果主要关注的是 不确定的初始值 据我所知这是完全合理的。此更改记录在 gcc 4.6 release notes:

In 4.6.0 and 4.6.1 G++ no longer allows objects of const-qualified type to be default initialized unless the type has a user-declared default constructor. In 4.6.2 G++ implements the proposed resolution of DR 253, so default initialization is allowed if it initializes all subobjects. Code that fails to compile can be fixed by providing an initializer e.g.

struct A { A(); };
struct B : A { int i; };
const B b = B();