空 class 是否可用作没有初始化器或显式默认构造函数的 constexpr 变量?
Is an empty class usable as a constexpr variable without an initializer or explicit default constructor?
给定以下代码:
struct f {
};
int main(){
constexpr f f1 ;
//const f f1 ; // This also has the same issue
//constexpr f f1 = {} ; //This works
}
clang 和 gcc 不同意它是否有效,clang 提供以下诊断 (see it live):
error: default initialization of an object of const type 'const f' without a user-provided default constructor
constexpr f f1 ;
^
{}
据我所知,f
是文字类型,它由隐式默认构造函数初始化,该构造函数应允许将其声明为 constexpr。谁是正确的?
注意,如果我显式添加 constexpr 默认构造函数,clang 会接受 f1
的声明:
constexpr f() {} ;
答案是否变化 f
不是聚合?
如果我们从 C++14 标准草案部分 7.1.5
[dcl.constexpr] 开始,我们可以发现 constexpr 对象声明的要求是:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have
literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19).
所以f
是文字类型吗?
第 3.9
[basic.types] 部分说:
A type is a literal type if it is:
并涵盖以下项目符号中的 classes:
a class type (Clause 9) that has all of the following properties
- it has a trivial destructor,
- it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and
- all of its non-static data members and base classes are of non-volatile literal types.
所以我们对第一个和第三个项目符号没问题。为了涵盖第二个项目符号,我们可以注意到 f
是一个 聚合 但如果我们稍微修改示例,例如如果 f
看起来像这样:
struct f {
private:
int x = 0 ;
} ;
这在 C++11 或 C++14 中都不是聚合,但问题仍然存在。然后我们需要证明它有一个 constexpr 构造函数。 f
是否有 constexpr 构造函数?
据我所知 12.1
[class.ctor] 部分说是:
[...] If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5),
the implicitly-defined default constructor is constexpr. [...]
但不幸的是,8.5
部分要求我们有一个用户提供的构造函数,它说:
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.
所以看起来 clang 在这里是正确的,如果我们查看以下 clang 错误报告:"error: default initialization of an object of const type 'const Z' requires a user-provided default constructor" even when no constructor needed. So clang is basing their lack of support for this due to defect report 253 目前没有建议的措辞,它说(强调我的):
Paragraph 9 of 8.5 [dcl.init] says:
If no initializer is specified for an object, and the object is of
(possibly cv-qualified) non-POD class type (or array thereof), the
object shall be default-initialized; if the object is of
const-qualified type, the underlying class type shall have a
user-declared default constructor. Otherwise, if no initializer is
specified for an object, the object and its subobjects, if any, have
an indeterminate initial value; if the object or any of its subobjects
are of const-qualified type, the program is ill-formed.
如果 const POD 对象没有非静态数据成员怎么办?对于这种情况,这种措辞需要一个空的初始值设定项
[...]
类似的注释适用于非 POD const 对象,其所有非静态数据成员和基础 class 子对象都有默认构造函数。为什么要求这样的对象的 class 具有用户声明的默认构造函数?
缺陷报告仍处于打开状态,但对其的最后评论是:
If the implicit default constructor initializes all subobjects, no initializer should be required.
还有注释:
This issue should be brought up again in light of constexpr constructors and non-static data member initializers.
请注意,自缺陷报告出现以来,8.5
部分中对 const 限定类型的限制有所改变。这是由于 C++11 之前的提议 N2762: Not so Trivial Issues with Trivial。
虽然缺陷报告仍然开放,但考虑到 constexpr 更改和非静态数据成员初始值设定项,它似乎不再是必要的限制。特别是考虑到对 constexpr 构造函数的以下要求:
- every non-variant non-static data member and base class sub-object shall be initialized (12.6.2);
给定以下代码:
struct f {
};
int main(){
constexpr f f1 ;
//const f f1 ; // This also has the same issue
//constexpr f f1 = {} ; //This works
}
clang 和 gcc 不同意它是否有效,clang 提供以下诊断 (see it live):
error: default initialization of an object of const type 'const f' without a user-provided default constructor
constexpr f f1 ;
^
{}
据我所知,f
是文字类型,它由隐式默认构造函数初始化,该构造函数应允许将其声明为 constexpr。谁是正确的?
注意,如果我显式添加 constexpr 默认构造函数,clang 会接受 f1
的声明:
constexpr f() {} ;
答案是否变化 f
不是聚合?
如果我们从 C++14 标准草案部分 7.1.5
[dcl.constexpr] 开始,我们可以发现 constexpr 对象声明的要求是:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19).
所以f
是文字类型吗?
第 3.9
[basic.types] 部分说:
A type is a literal type if it is:
并涵盖以下项目符号中的 classes:
a class type (Clause 9) that has all of the following properties
- it has a trivial destructor,
- it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
- all of its non-static data members and base classes are of non-volatile literal types.
所以我们对第一个和第三个项目符号没问题。为了涵盖第二个项目符号,我们可以注意到 f
是一个 聚合 但如果我们稍微修改示例,例如如果 f
看起来像这样:
struct f {
private:
int x = 0 ;
} ;
这在 C++11 或 C++14 中都不是聚合,但问题仍然存在。然后我们需要证明它有一个 constexpr 构造函数。 f
是否有 constexpr 构造函数?
据我所知 12.1
[class.ctor] 部分说是:
[...] If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined default constructor is constexpr. [...]
但不幸的是,8.5
部分要求我们有一个用户提供的构造函数,它说:
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.
所以看起来 clang 在这里是正确的,如果我们查看以下 clang 错误报告:"error: default initialization of an object of const type 'const Z' requires a user-provided default constructor" even when no constructor needed. So clang is basing their lack of support for this due to defect report 253 目前没有建议的措辞,它说(强调我的):
Paragraph 9 of 8.5 [dcl.init] says:
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
如果 const POD 对象没有非静态数据成员怎么办?对于这种情况,这种措辞需要一个空的初始值设定项
[...]
类似的注释适用于非 POD const 对象,其所有非静态数据成员和基础 class 子对象都有默认构造函数。为什么要求这样的对象的 class 具有用户声明的默认构造函数?
缺陷报告仍处于打开状态,但对其的最后评论是:
If the implicit default constructor initializes all subobjects, no initializer should be required.
还有注释:
This issue should be brought up again in light of constexpr constructors and non-static data member initializers.
请注意,自缺陷报告出现以来,8.5
部分中对 const 限定类型的限制有所改变。这是由于 C++11 之前的提议 N2762: Not so Trivial Issues with Trivial。
虽然缺陷报告仍然开放,但考虑到 constexpr 更改和非静态数据成员初始值设定项,它似乎不再是必要的限制。特别是考虑到对 constexpr 构造函数的以下要求:
- every non-variant non-static data member and base class sub-object shall be initialized (12.6.2);