为什么带有指针子对象的文字类类型的 constexpr 表达式不能是非类型模板参数

why a constexpr expression of literal class type with a pointer subobject can't be a non-type template argument

本文关键字:类型 表达式 constexpr 不能 是非 参数 指针 文字 对象 为什么      更新时间:2023-10-16

看了关于非类型模板参数的帖子后,我对那篇帖子中的一个例子感到困惑,我在这里引用了这个例子:

struct VariableLengthString {
const char *data_ = nullptr;
constexpr VariableLengthString(const char *p) : data_(p) {}
auto operator<=>(const VariableLengthString&) const = default;
};
template<VariableLengthString S>
int bar() {
static int i = 0;
return ++i;
}
int main() {
int x = bar<"hello">(); // ERROR
}

帖子说"相关措辞是[temp.arg.nontype]/2",所以我看了一下那个规则,它限制了什么可以是非类型模板参数。

非类型模板参数的模板参数

应是模板参数类型的转换常量表达式

所以,我看了一下converted constant expression,它的定义在这里:

类型 T 的转换常量表达式是一个表达式,

隐式转换为类型 T,其中转换后的表达式是常量表达式,隐式转换序列仅包含...

什么是常量表达式?这些规则在这里:

表达式 e 是核心常量表达式,除非 e 的计算遵循抽象机器的规则,将计算以下表达式之一:

(2.2( 对文字类的 constexpr 构造函数以外的函数的调用、constexpr函数或对普通析构函数的隐式调用。

那么,类类型VariableLengthString文字类吗?是的,这是,有什么规则可以证明这里:

类型是文本类型,如果它是:
1. 可能符合 cv 条件的 void;或 2. 标量类型;或 3. 引用类型;或
4. 文本类型的数组;或


可能符合 CV 条件的类类型,具有以下所有属性:

  1. 它有一个微不足道的析构函数,
  2. 它要么是闭包类型,要么是聚合类型,要么至少有一个不是复制或移动构造函数的 constexpr 构造函数或构造函数模板(可能继承自基类(,
  3. 如果它不是联合,则其所有非静态数据成员和基类都是非易失性文本类型。

关键点是,这些类型的 ojbects 类型的子对象VariableLengthString都是文字类型吗?类VariableLengthString是否至少有一个 constexpr 构造函数?是的,他们是。由于这些规则:

算术类型、枚举类型、指针

类型、指向成员类型的指针([basic.compound](、std::nullptr_t 和这些类型的 cv 限定版本统称为标量类型

所以,子对象data_,它是指针类型,因此,它是标量类型,也是一个文字类型。子弹 3 满意。constexpr VariableLengthString(const char *p)constexpr 构造函数吗?是的,确实如此,因为这些规则:

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

  1. 该类不得有任何虚拟基类;
  2. 每个参数类型应为文本类型;
  3. 应初始化每个非变量非静态数据成员和基类子对象

对于constexpr VariableLengthString(const char *p)来说,这三个规则都是满足的。综上所述,类VariableLengthString是文字类型,类型VariableLengthString的constexpr表达式可以作为非类型模板参数使用,因为它满足了作为转换常量表达式的要求。如果我错过了什么,请帮我找出来。

代码格式不正确,因为标准是这样说的:

对于引用或指针类型的非类型模板参数,

或者对于类类型或其子对象的非类型模板参数中引用或指针类型的每个非静态数据成员,引用或指针值不得引用或分别是以下地址:

  • 字符串文本

强调添加。C++17 及更早版本不允许将指向文本的指针(或引用(用作 NTTP。因此,C++20 不允许通过 NTTP 的类成员将指针(或引用(偷运到文本。

相关文章: