5

这是我的代码:

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 

根据dcl.constexpr

constexpr 函数的定义应满足以下要求: ...

  • 它的每个参数类型都应该是文字类型;...

basic#types.general-10

一个类型是文字类型,如果它是: ...

  • 它要么是闭包类型,要么是聚合类型,要么是...

我理解不是文字类型的原因agg_t2是它违反了规则dcl.init.aggr#1.1

聚合是一个数组或一个类...

  • 没有用户声明或继承的构造函数...

而且我认为agg_t1可能不是文字类型,因为它也违反了规则dcl.init.aggr#1.1

聚合是一个数组或一个类...

  • 没有私有或受保护的直接非静态数据成员...

但是......编译器结果告诉我我对agg_t1.

我的问题是:

如果agg_t1私有数据成员x使其成为非聚合类型,那么为什么在函数定义中agg_t1允许该类型?constexprce1

4

1 回答 1

0

如果agg_t1的私有数据成员x使其成为非聚合类型,那么为什么在 的函数定义中agg_t1允许该类型?constexprce1


C++20

agg_t1由于它的私有数据成员,它确实不是一个聚合类,但是虽然聚合性可能是类类型成为文字类型的充分要求之一,但它不是必需的。注意[basic.types.general]/10.5.2 [强调我的] 的or条件:

一个类型是文字类型,如果它是:

  • [...]
  • 具有以下所有属性的可能具有 cv 限定的类类型:
    • [...]
    • 要么是闭包类型([expr.prim.lambda.closure]),要么是聚合类型([dcl.init.aggr]),或者至少有一个 constexpr 构造函数或构造函数模板(可能继承([namespace.udecl] ) 来自基类) 不是复制或移动构造函数,[...]

根据[class.default.ctor]4

[...] 如果用户编写的默认构造函数满足 constexpr 构造函数 ([dcl.constexpr]) 的要求,则隐式定义的默认构造函数是 constexpr [...]

[dcl.constexpr]/3[dcl.constexpr]/4

/3 constexpr 函数的定义应满足以下要求:

  • [...]
  • 如果函数是构造函数或析构函数,则其类不应有任何虚拟基类;[...]

/4 函数体不是 = delete 的 constexpr 构造函数的定义还应满足以下要求:

  • 对于非委托构造函数,选择用于初始化非静态数据成员和基类子对象的每个构造函数都应为 constexpr 构造函数
  • [...]

agg_t1is的隐式定义的默认构造函数constexpr,因此 [basic.types.general]/10.5.2 不会取消agg_t1成为 C++20 中的文字类型的资格。


C++17

在 C++17 中,隐式定义的默认构造函数agg_t1is not 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;
};

接受以下内容:

// OK: B is a literal type.
class B {
    constexpr B() = default;  // OK
private:
    int x{};
};
于 2021-12-29T13:16:47.537 回答