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

L. *_* F. 11 c++ templates language-lawyer c++17 c++20

Yakk-Adam Nevraumont

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

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

(注意:正如在 回答 另一个评论的评论中所指出的 ,这是关于非类型模板参数,而不是模板类型参数模板参数可以是每个 [temp.arg.type]的有效type-id,包括。void*

L. *_* F. 14

TL; DR

类型的模板参数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表示:

如果给定type 的glvalue ,则类型C具有强结构相等性xconst C

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

  • C是的类型类型,其==运算符在的定义中定义为默认值Cx == x在上下文转换为时格式正确bool,所有C的基类子对象和非静态数据成员都具有很强的结构相等性,并且C没有mutablevolatile子对象。

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

如果复合指针类型是对象指针类型p <=> q则类型为std::strong_­ordering。如果两个指针操作数p并且q 比较等于([expr.eq]),则p <=> q得出 std::strong_­ordering::equal; 如果pq比较不相等,则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中,模板参数不得具有type void*

C ++ 11和C ++ 14的措辞相同,只是不存在有关占位符类型的符号。通常, 在C ++ 20之前,模板参数不得具有type void*

但是编译器会诊断吗?

TC评论中 说,没有人诊断出该IHRC。让我们用下面显示的最小示例来测试编译器是否以C ++ 17模式进行诊断:

template <void*>
class C {};

int main()
{
    C<nullptr> x;
    (void) x;
}
Run Code Online (Sandbox Code Playgroud)

该代码可以在GCC 9.1.0GCC 8.3.0GCC 7.3.0GCC 6.3.0GCC 5.5.0Clang 8.0.0Clang 7.0.0Clang 6.0.1Clang 5.0上编译并正常运行 .0

NathanOliver评论中告诉我,有人告诉他某些编译器会出错,但是主要的编译器不会出错。因此,据我所能确认的,TC的说法是正确的-没有人能诊断出这一点。

  • @Joshua这是关于`template &lt;void *&gt; struct X {};`-而不是`template &lt;typename T&gt; struct Y {}; Y &lt;void *&gt;();` (7认同)
  • 没有人能诊断出IIRC。 (2认同)
  • 这甚至在说什么?我一直能够做vector &lt;void *&gt;。 (2认同)