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

时间:2015-04-16 18:43:29

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

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

struct S {
    int a = 1;
};

当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

以下尝试都没有对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或聚合?

2 个答案:

答案 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. 就现行标准而言,GCC不合规;见上文。

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

      

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

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

  3. GCC开发人员(例如here)暗示,由于委员会基本同意上述决议,海湾合作委员会目前的行为是可行的,不应调整。所以人们可以说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();