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

12

Yakk - Adam Nevraumont :

在标准的某些版本中,不允许使用类型为void*的非类型模板参数。

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

(注:如答案另一条评论所述, 这是关于非类型模板参数, 而不是模板类型参数, 可以使用任何有效的type-id作为 [temp.arg.type], 包括void*

1个回答

16

简而言之

C++20之后,void*类型的模板参数是合法的,但在C++20之前则是非法的。

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的glvalue x, 要么:

  • C是非类类型,并且x <=> x是一个类型为std::strong_orderingstd::strong_equality的有效表达式,或者

  • C是一个类类型,其==运算符在定义C时默认为已定义,当上下文转换为bool时,x == x是良好的形式, 所有C的基类子对象和非静态数据成员具有强结构相等性,并且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大于p并且p <=> q产生std::strong_­ordering::greater,如果p大于q。否则,结果未指定。
请注意,void*是一个对象指针类型([basic.compound]/3)。因此,x <=> xstd::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.评论中表示没有人诊断IHRC。让我们通过下面的最小示例测试编译器是否在C++17模式下诊断出这个问题。
template <void*>
class C {};

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

该代码在以下编译器上编译和运行正常:

NathanOliver 在一篇评论中表示有人告诉他某些编译器会出错,但主要的编译器不会。因此,据我所知,在这里无人对T.C.的说法进行了验证。


2
没人确诊过,尽管我记得。 - T.C.
2
这到底是在说什么?我一直都能够使用vector<void*> - Joshua
7
这是关于template <void*> struct X { };的问题,而不是关于template <typename T> struct Y { }; Y<void*>();的问题。 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接