提问者:小点点

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


首先,我有一个结构,它有一个值和一个默认值

struct S {
    int a = 1;
};

当gcc和clang都使用非常量/非常量表达式时,可以默认构造此类型。在这两种情况下,std::is_pod

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{}时失败尤其是当类型不是聚合时。

标准让我觉得叮当声是对的
N4296:8.5/7

如果程序调用const限定类型T的对象的默认初始化,则T应该是具有用户提供的默认构造函数的类类型

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


共2个答案

匿名用户

const S s3;

包含在[dcl.init]/12中:

如果未为对象指定初始化器,则默认初始化该对象。

因此,根据您的报价要求,必须存在用户提供的默认构造函数。加一个这样的

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

然后使声明编译罚款。

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

S在您的案例中是一个聚合,并且const S{}的原因是有效的。聚合初始化应用于常量S{},一切正常
如果S不是聚合,

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

  • 如果T是聚合,则执行聚合初始化(8.5.1)

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

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

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

由于成员有一个初始值设定项([class.ctor]/4.9),因此默认的ctor实际上是不重要的,但这与约束的检查方式无关。因此它是默认初始化,并且

const S s{};

同样有效(或无效!)像

const S t;

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

好:

>

  • 就目前的标准而言,海湾合作委员会不符合;见上文。

    有一个活跃的CWG问题,即15年前创建的253,涵盖了类似的场景。2011年一次会议的最后一条记录是

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

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

    海湾合作委员会的开发者(例如这里)暗示,由于委员会基本上同意上述决议,海湾合作委员会目前的行为是可行的,不应该调整。因此,人们很可能会认为海湾合作委员会是对的,标准被打破了。

  • 匿名用户

    所以看起来gcc是基于DR 253的,尽管这个问题还没有解决。我们可以从以下gcc错误报告中看到这一点,报告中说:

    这是出于设计,因为正如Dr253所示,规范性标准存在缺陷。

    gcc的变化使其生效,它说:

    Core 234-如果默认构造函数初始化所有子对象,则允许常量对象不带初始化器或用户提供的默认构造函数。

    因此,从技术上讲,clang是正确的,gcc是不一致的,但似乎他们相信dr253将得到有利于他们的解决。这是完全有意义的,如果主要关注的是不确定的初始值,据我所知是。此变更记录在gcc 4.6发行说明中:

    在4.6中。0和4.6。1 G不再允许对const限定类型的对象进行默认初始化,除非该类型具有用户声明的默认构造函数。在4.6中。2G实现了DR 253的建议分辨率,因此如果它初始化所有子对象,则允许默认初始化。无法编译的代码可以通过提供初始值设定项来修复,例如。

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