const T {}; 作品,const T; 当T是非POD时失败,

Rya*_*ing 24 c++ gcc clang language-lawyer c++14

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

struct S {
    int a = 1;
};
Run Code Online (Sandbox Code Playgroud)

当gcc和clang都是非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
Run Code Online (Sandbox Code Playgroud)

以下尝试都没有对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
};
Run Code Online (Sandbox Code Playgroud)

我唯一可以做的就是constexpr S() { }明确添加这项工作.看来真的错了,我认为const S s;虽然失败const S s{};,特别是当类型不是聚集.

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

如果程序要求对const限定类型T的对象进行默认初始化,则T应为具有用户提供的默认构造函数的类类型

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

Col*_*mbo 16

const S s3;
Run Code Online (Sandbox Code Playgroud)

[dcl.init]/12涵盖:

如果没有为对象指定初始化程序,则默认初始化该对象.

因此,根据引用的要求,必须存在用户提供的默认构造函数.添加一个这样的

struct S {
    int a = 1;
    constexpr S(){}
};
Run Code Online (Sandbox Code Playgroud)

然后使声明编译正常.

[..]特别是当类型不是聚合时.

S 在您的情况下是一个聚合,其原因const S s{}是有效的.应用聚合初始化const S s{},一切都很好.
如果S不是聚合,

列表初始化对象或类型T的引用定义如下:

  • 如果T是聚合,则执行聚合初始化(8.5.1).
  • 否则,如果初始化列表没有元素并且T是具有默认构造函数的类类型,则对象将进行值初始化.

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

值初始化类型的对象T意味着:

  • if T是一个(可能是cv限定的)类类型(第9条),没有默认构造函数(12.1)或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的;
  • 如果T是没有用户提供或删除的默认构造函数的(可能是cv限定的)类类型,则该对象被零初始化并且检查默认初始化的语义约束,并且如果 T具有非平凡的默认构造函数,则对象是默认初始化的;

默认的ctor确实非常重要,因为一个成员有一个初始化器([class.ctor] /4.9),但这是无关紧要的,因为无论如何都要检查约束.因此默认初始化它和行

const S s{};
Run Code Online (Sandbox Code Playgroud)

与...一样有效(或无效!)

const S t;
Run Code Online (Sandbox Code Playgroud)

那么为什么gcc允许这样做呢

好:

  1. 就现行标准而言,GCC不符合要求; 往上看.

  2. 有一个活跃的CWG问题,#253,创建于十五年前,涵盖了类似的情况.2011年会议上关于这一点的最后一点说明

    如果隐式默认构造函数初始化所有子对象,则不需要初始化程序.

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

  3. 海湾合作委员会开发商(例如此处)暗示,由于委员会基本同意上述决议,海湾合作委员会目前的行为是可行的,不应进行调整.所以人们可以说GCC是正确的,标准被打破了.


Sha*_*our 5

所以看起来gcc正基于DR 253,即使这还没有解决.我们可以从以下gcc错误报告中看到这一点:

这是设计上的,因为正如DR 253所示,规范标准存在缺陷.

而实现这一点gcc变化说:

核心234 - 如果默认构造函数初始化所有子对象,则允许const对象不使用初始化程序或用户提供的默认构造函数.

所以从技术上讲clang是正确的,gcc并且不符合要求,但似乎他们相信DR 253会得到有利于他们的解决.如果主要关注点是不确定的初始值,这是完全合理的,据我所知.gcc 4.6发行说明中记录了此更改:

在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();
Run Code Online (Sandbox Code Playgroud)