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

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

问题描述:

查看 the 关于非类型模板参数的帖子,我对该帖子中的示例感到困惑,在此引用示例:

After looking at the post about non-type template argument,I have a confusion for an example in that post,I cite the example here:

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",所以我看了一下这个规则,它限制了什么可以是非类型模板参数.

The post says "the relevant wording is [temp.arg.nontype]/2",So I took a look at that rule,It constrains what can be a non-type template argument.

非类型模板参数的模板参数应为模板参数类型的转换常量表达式.

所以,我看了一下转换后的常量表达式,它的定义在这里:

So,I took a look at converted constant expression,Its definitions are here:

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

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only...

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

What is a constant expression?These rules are here:

表达式e是核心常量表达式,除非按照抽象机的规则对e求值将对以下表达式之一求值:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

(2.2)调用除文字类的constexpr构造函数以外的函数,constexpr函数或平凡的析构函数的隐式调用.

(2.2) an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor.

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

So,Is the class type VariableLengthString a literal class?Yes,it is.what rules can prove that are here:

如果是,则类型是文字类型:
1.合格的或
2.标量类型;或
3.参考类型;或
4.文字类型的数组;或
可能具有cv资格的类类型,具有以下所有属性:

A type is a literal type if it is:
1. possibly cv-qualified void; or
2. a scalar type; or
3. a reference type; or
4. an array of literal type; or
a possibly cv-qualified class type that has all of the following properties:

  1. 它有一个琐碎的析构函数
  2. 它是闭包类型,聚合类型,或者至少具有一个constexpr构造函数或构造函数模板(可能从基类继承),而不是复制或移动构造函数,
  3. 如果不是联合,则其所有非静态数据成员和基类均为非易失性文字类型.

关键是,这些类型为 VariableLengthString 的对象的子对象类型是否都是文字类型?类 VariableLengthString 是否至少有一个constexpr构造函数?是的.因为这些规则:

The key point is that,Are these types of sub-objects of ojbects of type VariableLengthString all literal types? Does the class VariableLengthString have at least one constexpr constructor?Yes they are.Because of these rules:

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

Arithmetic types, enumeration types, pointer types, pointer to member types ([basic.compound]), std​::​nullptr_­t, and cv-qualified versions of these types are collectively called scalar types.

因此,子对象 data _ 是指针类型.因此,它是标量类型,也是文字类型.满足项目符号3.是否 constexpr VariableLengthString(const char *p)一个constexpr构造函数?是的,因为这些规则:

So,the sub-object data_,it's of type pointer.hence,it's scalar type,also a literal type.The bullet 3 is satisfied.Does constexpr VariableLengthString(const char *p) a constexpr constructor?Yes,it is,Because of these rules:

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

The definition of a constexpr constructor shall satisfy the following requirements:

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

对于 constexpr VariableLengthString(const char * p),这三个规则都得到满足.总之, VariableLengthString 类是一个文字类型和constexpr表达式类型 VariableLengthString 可以用作非类型模板参数,因为它满足转换后的常量表达式的要求.为什么上面的代码格式错误?错过了一些,请帮助我找出它们.

For constexpr VariableLengthString(const char *p),these three rules are all be satisfied.In summary,the class VariableLengthString is a literal type and a constexpr expression of type VariableLengthString could be used as a non-type template argument,because it satisfies the requirement of being a converted constant expression.why the code above is ill-formed?If I miss something,please help me find out them.

该代码格式错误,因为

The code is ill-formed because the standard says so:

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

For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

...

  • 字符串文字

已添加重点.C ++ 17和更低版本不允许您将指向文字的指针用作NTTP.因此,C ++ 20不允许您通过NTTP的类成员 smuggle 指向文字的指针.

Emphasis added. C++17 and before would not allow you to use a pointer to a literal as an NTTP. C++20 therefore doesn't allow you to smuggle a pointer to a literal through a class member of an NTTP.