【问题标题】:const T{}; works, const T; fails when T is a non-POD,常量 T{};作品,常量T;当 T 是非 POD 时失败,
【发布时间】:2015-06-23 08:54:11
【问题描述】:

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

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

以下尝试都不会影响 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

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

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

【问题讨论】:

标签: c++ gcc clang language-lawyer c++14


【解决方案1】:
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 是具有默认构造函数的类类型,则该对象是值初始化的。

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

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

  • 如果T 是 (可能是 cv 限定的)类类型(第 9 条),没有默认值 构造函数(12.1)或用户提供或删除的默认构造函数,则对象被默认初始化;
  • 如果T 是一个(可能 cv-qualified) 没有用户提供或删除的默认值的类类型 构造函数,然后 对象是零初始化的,并检查默认初始化的语义约束,如果 T 有一个重要的默认构造函数,对象是默认初始化的;

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

const S s{};

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

那么为什么 gcc 允许这样做

嗯:

  1. 就现行标准而言,GCC 不符合标准;见上文。

  2. 有一个活跃的 CWG 问题,#253,创建于 15 年前,涵盖了类似的场景。 2011 年会议上关于这个的最后说明说

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

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

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

【讨论】:

  • gcc.gnu.org/bugzilla/show_bug.cgi?id=57820gcc.gnu.org/bugzilla/show_bug.cgi?id=60284。 GCC 正在实施 CWG 253 的预期决议。
  • @T.C.故意地?但即便如此,它也不符合标准......他们不应该在这个问题关闭之前“修复”这个错误吗?
  • 当您知道标准将被更改以允许此类代码时,投入时间更改编译器以破坏用户代码的意义何在?
  • 另见 Jonathan Wakely 的评论 here
  • @Yakk -reasonably-pedantic?
【解决方案2】:

所以看起来 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();

【讨论】:

    猜你喜欢
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-02
    • 2015-04-24
    • 2012-06-09
    • 1970-01-01
    相关资源
    最近更新 更多