TL;DR
Template parameters of type void*
are valid since C++20.
They are invalid prior to C++20.
C++20
C++20 relaxed the restrictions on the type of a non-type template parameter,
so let's investigate it first.
The current draft (as of UTC 10:00, May 6, 2019) says in [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*
is a pointer type.
A pointer type is a scalar type ([basic.types]/9).
A scalar type is a literal type ([basic.types]/10).
Therefore, void*
is a literal type.
The first bullet is the relevant one.
Tracking down further, [class.compare.default]/3 says:
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*
is a non-class type,
so the first bullet is relevant.
Now the question boils down to the type of x <=> x
where x
is a glvalue of type void* const
(not const void*
).
Per [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.
Note that void*
is an object pointer type ([basic.compound]/3).
Therefore, x <=> x
is of type std::strong_ordering
.
Thus the type void*
has strong structural equality.
Therefore, in the current C++20 draft,
void*
is allowed as the type of a template parameter type.
C++17
Now we address C++17.
[temp.param] says:
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.
Note that "pointer to object" doesn't include void*
per [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 of the above six bullets include void*
as a possible type of a template parameter.
Therefore, in C++17,
a template parameter shall not have type void*
.
The wording is the same for C++11 and C++14
except that the bullet about placeholder types are not there.
In general,
prior to C++20,
a template parameter shall not have type void*
.
But do compilers diagnose this?
T.C. says in a
comment
that nobody diagnoses this IHRC.
Let's test whether compilers diagnose that in C++17 mode
with the minimal example shown below:
template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}
The code compiles and runs fine on
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,
and Clang 5.0.0.
NathanOliver told me in a comment that someone told him some compilers will error, but the major ones don't. Therefore, as far as I am able to confirm here, T.C.'s statement is correct — nobody diagnoses this.