const T {}; works,const T;当T是非POD时失败, [英] const T{}; works, const T; fails when T is a non-POD,

查看:120
本文介绍了const T {}; works,const T;当T是非POD时失败,的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  struct S {
int a = 1;
};

这种类型可以是默认构造的,当它是非const /非constexpr由gcc和铛。在两者下, std :: is_pod< S> :: value false 。奇怪的行为如下:

  S s1; // works under both 
const S s2 {}; // works under both
const S s3; //仅在gcc中工作,clang需要用户提供的构造函数

区别到clang:

  struct S {
int a = 1;
constexpr S()= default; // defaulted ctor
virtual void f(){} //虚函数,而不是聚合
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或聚合?

解决方案

  const S s3; 

涵盖于[dcl.init] / 12:


如果没有为一个对象指定初始化器,那么该对象是默认初始化的。


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

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

然后使声明编译正确


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

S 是总计为什么 const S s {} 有效。聚合初始化应用于 const S s {} ,一切正常。

如果 S 不是一个聚合,


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




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


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


value-initialize 类型 T 的对象意味着: p>


  • 如果 T
    (可能是cv限定的)类类型条款9)没有默认的
    构造函数(12.1)或者用户提供或删除的默认构造函数,那么对象是默认初始化的;

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


默认ctor确实是不重要的,因为成员有一个初始化器([class.ctor] /4.9),但是这是无关紧要的,因为这些约束被检查。因此默认初始化是,和行

  const S s {}; 

一样有效(或无效!)

  const S t; 




那么为什么gcc允许这个


好:


  1. GCC不合规;见上文。


  2. 有一个活动的CWG问题, #253 (十五年前创建),涵盖类似的情况。 2011年会议的最后一个注释是


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

    这是 S 的隐式默认构造函数的情况,


  3. GCC开发人员(例如这里)意味着由于委员会基本上同意上述决议,当前行为的GCC是可行的,不应该调整。所以人们可以很好地认为GCC是正确的,标准是破碎的。



To start with, I have a struct with one value with a default value

struct S {
    int a = 1;
};

This type can be default constructed when it is non-const / non-constexpr by both gcc and clang. Under both, std::is_pod<S>::value is false. The weird behavior is as follows:

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 of the following attempts makes a difference to 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
};

The only thing I can do that makes this work is to add constexpr S() { } explicitly. It seems really wrong to me that const S s; fails while const S s{}; especially when the type is not an aggregate.

The standard makes me think that Clang is right
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

So why does gcc allow this, and is S{}; not default initializing, even when the type is not a POD or an aggregate?

解决方案

const S s3;

Is covered by [dcl.init]/12:

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

Thus, as required by your quote, a user-provided default constructor must be present. Adding one like so

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

then makes the declaration compile fine.

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

S is an aggregate in your case, and the reason why const S s{} is valid. Aggregate initialization is applied for const S s{}, and everything's fine.
If S is not an aggregate,

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.

Now consider the definition of value initialization:

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;

The default ctor is indeed nontrivial since a member has got an initializer ([class.ctor]/4.9), but that's irrelevant since the constraints are checked eitherway. Hence default-initialization it is, and the line

const S s{};

Is just as valid (or invalid!) as

const S t;

So why does gcc allow this

Well:

  1. Speaking in terms of the current standard, GCC is not compliant; See above.

  2. There is an active CWG issue, #253, created fifteen years ago, that covers a similar scenario. The final note on this one from a 2011 meeting says

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

    That is the case with the implicit default constructor for S, and this would make all your lines valid.

  3. GCC developers (e.g. here) implied that since the committee basically agreed upon that above resolution, the current behavior of GCC is feasible and should not be adjusted. So one could well argue that GCC is right and the standard is broken.

这篇关于const T {}; works,const T;当T是非POD时失败,的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆