在标准的某些版本中,不允许使用类型为
void*
的非类型模板参数。
这是真的吗?
如果是真的,在哪些标准版本中不允许使用类型为void*
的非类型模板参数?
(注:如答案另一条评论中
所述,
这是关于非类型模板参数,
而不是模板类型参数,
可以使用任何有效的type-id作为
[temp.arg.type],
包括void*
。
在标准的某些版本中,不允许使用类型为
void*
的非类型模板参数。
这是真的吗?
如果是真的,在哪些标准版本中不允许使用类型为void*
的非类型模板参数?
(注:如答案另一条评论中
所述,
这是关于非类型模板参数,
而不是模板类型参数,
可以使用任何有效的type-id作为
[temp.arg.type],
包括void*
。
C++20之后,void*
类型的模板参数是合法的,但在C++20之前则是非法的。
C++20放宽了非类型模板参数的类型限制,下面我们来详细了解一下。
当前草案(截至2019年5月6日UTC 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
具有强结构相等性,则给定类型为const C
的glvaluex
, 要么:
C
是非类类型,并且x <=> x
是一个类型为std::strong_ordering
或std::strong_equality
的有效表达式,或者
C
是一个类类型,其==
运算符在定义C
时默认为已定义,当上下文转换为bool
时,x == x
是良好的形式, 所有C
的基类子对象和非静态数据成员具有强结构相等性,并且C
没有mutable
或volatile
子对象。
void*
是一种非类类型,因此第一个要点是相关的。
现在问题归结为x <=> x
的类型,
其中x
是类型为void* const
(而不是const void*
)的glvalue。
根据[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
并且p <=> q
产生std::strong_ordering::greater
,如果p
大于q
。否则,结果未指定。void*
是一个对象指针类型([basic.compound]/3)。因此,x <=> x
是std::strong_ordering
类型。因此,类型void*
具有强结构相等性。void*
允许作为模板参数类型的类型。
std::nullptr_t
,
或者包含占位符类型的类型。void*
,根据[basic.compound]/3:
[注:指向
void
的指针没有指向对象类型,因为void
不是对象类型。-注释结束]
以上六个要点中没有一个包括void*
作为模板参数的可能类型。因此,在C++17中,模板参数不得具有void*
类型。
C++11和C++14的措辞相同,只是没有占位符类型的那个要点。一般来说,在C++20之前,模板参数不应具有void*
类型。
template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}
该代码在以下编译器上编译和运行正常:
NathanOliver 在一篇评论中表示有人告诉他某些编译器会出错,但主要的编译器不会。因此,据我所知,在这里无人对T.C.的说法进行了验证。
vector<void*>
。 - Joshuatemplate <void*> struct X { };
的问题,而不是关于template <typename T> struct Y { }; Y<void*>();
的问题。 - Barry