非类型模板参数可以是 "void*" 类型吗?

Can a non-type template parameter be of type "void*"?

本文关键字:类型 void 参数      更新时间:2023-10-16

Yakk - Adam Nevraumont说:

不允许在void*至少是标准的某些版本。

这是真的吗? 如果为 true,则不允许在哪些版本的标准中使用void*类型的非类型模板参数?

(注:如 一条评论 回答 另一条评论, 这是关于非类型模板参数, 不是模板类型参数, 可以是每个[温度参数类型], 包括void*.

TL;博士

类型为void*的模板参数自 C++20 起有效。 它们在 C++20 之前无效。

C++20

C++20 放宽了对非类型模板参数类型的限制, 因此,让我们先调查一下。

当前草案(截至 UTC 2019 年 5 月 6 日 10:00)在 [temp.param]/4 中说:

非类型模板参数应具有以下项之一 (可选符合 CV 标准)类型:

  • 具有强结构相等性的文本类型([class.compare.default]),
  • 左值引用类型,
  • 包含占位符类型 ([dcl.spec.auto]) 的类型,或
  • 推导类类型的占位符 ([dcl.type.class.deduct])。

void*是指针类型。 指针类型是标量类型([basic.types]/9)。标量类型文本类型([basic.types]/10)。 因此,void*文本类型。 第一个项目符号是相关的项目符号。

进一步追踪,[class.compare.default]/3 说:

具有强结构相等性的C,如果给定 glvaluex键入const C,任:

  • C是非类类型,x <=> x是类型std::strong_orderingstd::strong_equality的有效表达式,或

  • C是一个类类型,其==运算符定义为C定义中的默认值,x == x在上下文中格式良好 转换为boolC的所有基类子对象和非静态 数据成员具有很强的结构平等性,C没有mutablevolatile子对象。

void*是非类类型, 所以第一个项目符号是相关的。 现在问题归结为x <=> x的类型 其中x是类型为void* const(不是const void*)的 glvalue 。 每 [expr.spaceship]/8:

如果复合指针类型是对象指针类型,则p <=> q类型std::strong_­ordering.如果两个指针操作数pq比较相等([expr.eq]),p <=> q收益率std::strong_­ordering::equal;如果pq比较不相等,则p <=> q产生std::strong_­ordering::less如果q大于pstd::strong_­ordering::greater如果p比较大于q([expr.rel])。否则,结果未指定。

请注意,void*对象指针类型([basic.compound]/3)。 因此,x <=> x属于std::strong_ordering型。 因此,void*型具有很强的结构平等性。

因此,在目前的C++20草案中, 允许void*作为模板参数类型的类型。

C++17

现在我们解决C++17。 [温度参数] 说:

非类型模板参数应具有以下项之一 (可选符合 CV 标准)类型:

  • 整数或枚举类型,
  • 指向
  • 对象的指针或指向函数的指针,
  • 对对象的左值引用
  • 或对函数的左值引用,
  • 指向成员的指针,
  • std​::​nullptr_­t
  • 包含占位符类型的类型。

请注意,"指向对象的指针"不包括void*每 [基本化合物]/3:

[注意:但是,指向void的指针没有指向对象的指针类型,因为void不是对象类型。

以上六个项目符号均不包括void*作为模板参数的可能类型。 因此,在第 17 C++, 模板参数不应具有类型void*

C++11 和 C++14 的措辞相同 除了关于占位符类型的项目符号不存在。 通常在C++20之前, 模板参数不应具有类型void*

但是编译器会诊断出这一点吗?

T.C.在 评论 没有人诊断这个IHRC。 让我们测试编译器是否在 C++17 模式下诊断出这一点 最小示例如下所示:

template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}

代码编译并运行良好 GCC 9.1.0, GCC 8.3.0, 海湾合作委员会 7.3.0, GCC 6.3.0, GCC 5.5.0, 叮当 8.0.0, 叮当 7.0.0, 叮当 6.0.1, 和 Clang 5.0.0。

NathanOliver在评论中告诉我,有人告诉他一些编译器会出错,但主要的编译器不会。 因此,据我在这里能够证实,T.C.的说法是正确的——没有人诊断这一点。