TL;DR
void* 类型的模板参数自 C++20 起有效。
在 C++20 之前它们是无效的。
C++20
C++20 放宽了对非类型模板参数类型的限制,
所以让我们先调查一下。
当前草案(截至 2019 年 5 月 6 日 10:00 UTC)在[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 说:
如果给定一个左值 x
输入const C,或者:
void* 是非类类型,
所以第一个项目符号是相关的。
现在问题归结为x <=> x 的类型
其中x 是void* const 类型的左值(不是const void*)。
每[expr.spaceship]/8:
如果复合指针类型是对象指针类型,p <=> q 是
std::strong_ordering 类型。如果两个指针操作数p 和q
比较相等 ([expr.eq]),p <=> q 产生
std::strong_ordering::equal;如果p 和q 比较不相等,则p <=>
q 产生std::strong_ordering::less 如果q 比较大于
p 和 std::strong_ordering::greater 如果 p 比较大于
q ([expr.rel])。否则,结果未指定。
注意void* 是一个对象指针类型 ([basic.compound]/3)。
因此,x <=> x 的类型为 std::strong_ordering。
因此void* 类型具有强结构相等性。
因此,在当前的 C++20 草案中,
void* 允许作为模板参数类型的类型。
C++17
现在我们讨论 C++17。
[temp.param] 说:
非类型模板参数应具有以下之一
(可选 cv 限定)类型:
- 整数或枚举类型,
- 指向对象的指针或指向函数的指针,
- 对对象的左值引用或对函数的左值引用,
- 指向成员的指针,
-
std::nullptr_t,或
- 包含占位符类型的类型。
请注意,“指向对象的指针”不包括 void*
每[basic.compound]/3:
[ 注意:然而,指向void 的指针没有指向对象的类型,因为void 不是对象类型。 — 尾注 ]
以上六个项目符号都不包括void*
作为模板参数的可能类型。
因此,在 C++17 中,
模板参数的类型不得为void*。
C++11 和 C++14 的措辞相同
除了关于占位符类型的项目符号不存在。
一般来说,
在 C++20 之前,
模板参数的类型不得为void*。
但是编译器会诊断这个吗?
T.C. 说
comment
没有人诊断出这个 IHRC。
让我们测试一下编译器是否在 C++17 模式下诊断出来
用下面显示的最小示例:
template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}
代码编译并运行良好
GCC 9.1.0,
GCC 8.3.0,
GCC 7.3.0,
GCC 6.3.0,
GCC 5.5.0,
Clang 8.0.0,
Clang 7.0.0,
Clang 6.0.1,
和Clang 5.0.0。
NathanOliver 在comment 中告诉我,有人告诉他一些编译器会出错,但主要的编译器不会。因此,据我在这里可以确认,T.C. 的说法是正确的——没有人对此做出诊断。