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

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

Yakk - Adam Nevraumont :

Non-type template parameters of type void* are not allowed in at least some versions of the standard.

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

(注意:如 回答 , 这是关于非类型模板参数, 不是 模板类型参数, 可以是任何有效的 type-id [temp.arg.type], 包括 void*.

TL;DR

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

C++20

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

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

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • a literal type that has strong structural equality ([class.compare.default]),
  • an lvalue reference type,
  • a type that contains a placeholder type ([dcl.spec.auto]), or
  • a placeholder for a deduced class type ([dcl.type.class.deduct]).

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

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

A type C has strong structural equality if, given a glvalue x of type const C, either:

  • C is a non-class type and x <=> x is a valid expression of type std::strong_ordering or std::strong_equality, or

  • C is a class type with an == operator defined as defaulted in the definition of C, x == x is well-formed when contextually converted to bool, all of C's base class subobjects and non-static data members have strong structural equality, and C has no mutable or volatile subobjects.

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

If the composite pointer type is an object pointer type, p <=> q is of type std::strong_­ordering. If two pointer operands p and q compare equal ([expr.eq]), p <=> q yields std::strong_­ordering::equal; if p and q compare unequal, p <=> q yields std::strong_­ordering::less if q compares greater than p and std::strong_­ordering::greater if p compares greater than q ([expr.rel]). Otherwise, the result is unspecified.

请注意 void* 是一个 对象指针类型 ([basic.compound]/3)。 因此,x <=> xstd::strong_ordering 类型。 因此类型 void* 具有 强结构等式 .

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

C++17

现在我们介绍 C++17。 [temp.param] 说:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std​::​nullptr_­t, or
  • a type that contains a placeholder type.

请注意 "pointer to object" 不包括 void*[basic.compound]/3:

[ Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. — end note ]

None 以上六个项目符号包括 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;
}

代码编译并运行良好 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 told me in a 有人告诉他有些编译器会出错,但主要的不会。因此,就我在这里可以确认的而言,T.C. 的说法是正确的——没有人对此进行诊断。