abs*_*suu 5 c++ language-lawyer constant-expression constexpr constexpr-function
这是我的代码:
class agg_t1{
int x; // private non-static data menber
};
class agg_t2{
agg_t2(){} // user-provided constructor
};
constexpr void ce1(agg_t1 arg){}; // OK
constexpr void ce2(agg_t2 arg){}; // ERROR: parameter type 'agg_t2' is not a literal type
Run Code Online (Sandbox Code Playgroud)
constexpr 函数的定义应满足以下要求: ...
- 它的每个参数类型都应该是文字类型;...
如果类型是文字类型,则它是: ...
- 它可以是闭包类型、聚合类型,或者......
我理解为什么agg_t2不是文字类型的原因是,它违反了规则dcl.init.aggr#1.1:
聚合是一个数组或一个类......
- 没有用户声明或继承的构造函数...
我认为agg_t1可能不是文字类型,因为它也违反了规则dcl.init.aggr#1.1:
聚合是一个数组或一个类......
- 没有私有或受保护的直接非静态数据成员...
然而......编译器结果告诉我我对 的假设是错误的agg_t1。
如果agg_t1的私有数据成员x使其成为非聚合类型,那么为什么在的函数定义agg_t1中允许该类型?constexprce1
如果
agg_t1的私有数据成员x使其成为非聚合类型,那么为什么在的函数定义agg_t1中允许该类型?constexprce1
agg_t1由于其私有数据成员,它确实不是一个聚合类,但是,虽然聚合性可能是类类型成为文字类型的充分要求之一,但它不是必要的。注意[basic.types.general]/10.5.2 [强调我的]的or条件:
如果某个类型满足以下条件,则该类型是文字类型:
- [...]
- 可能是 cv 限定的类类型,具有以下所有属性:
- [...]
- 它可以是闭包类型 ([expr.prim.lambda.closure])、聚合类型 ([dcl.init.aggr]),或者至少有一个 constexpr 构造函数或构造函数模板(可能是继承的 ([namespace.udecl] )来自基类)不是复制或移动构造函数,[...]
[...] 如果用户编写的默认构造函数满足 constexpr 构造函数 ([dcl.constexpr]) 的要求,则隐式定义的默认构造函数为 constexpr [...]
和[dcl.constexpr]/3和[dcl.constexpr]/4:
/3 constexpr函数的定义应满足以下要求:
- [...]
- 如果函数是构造函数或析构函数,则其类不得有任何虚拟基类;[...]
/4 函数体不为=delete的constexpr构造函数的定义还应满足以下要求:
- 对于非委托构造函数,选择用于初始化非静态数据成员和基类子对象的每个构造函数都应是 constexpr 构造函数;
- [...]
的隐式定义的默认构造函数是agg_t1,constexpr因此 [basic.types.general]/10.5.2 并不取消agg_t1作为 C++20 中的文字类型的资格。
在 C++17 中,隐式定义的默认构造函数agg_t1不是constexpr,因为它违反了[dcl.constexpr]/4.5:
constexpr 构造函数的定义应满足以下约束:
- [...]
另外,它的函数体要么是=delete,要么满足以下约束:
- [...]
- /4.5 每个非变体非静态数据成员和基类子对象都应初始化([class.base.init]);
事实上,虽然以下内容被 Clang 和 GCC 拒绝-std=c++17:
class A {
constexpr A() = default; // error: cannot be constexpr
private:
int x;
};
Run Code Online (Sandbox Code Playgroud)
接受以下内容:
// OK: B is a literal type.
class B {
constexpr B() = default; // OK
private:
int x{};
};
Run Code Online (Sandbox Code Playgroud)