Yakk - Adam Nevraumont said:
Non-type template parameters of type
void*
are not allowed in at least some versions of the standard.
Is this true?
If it is true, in which versions of the standard are non-type template parameters of type void*
not allowed?
(Note: as noted in
a comment
to answer
another comment,
this is about non-type template parameters,
not template type arguments,
which can be any valid type-id per
[temp.arg.type],
including void*
.
Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
Non-type template arguments are normally used to initialize a class or to specify the sizes of class members. For non-type integral arguments, the instance argument matches the corresponding template parameter as long as the instance argument has a value and sign appropriate to the parameter type.
Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
Template parameters of type void*
are valid since C++20.
They are invalid prior to 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 glvaluex
of typeconst C
, either:
C
is a non-class type andx <=> x
is a valid expression of typestd::strong_ordering
orstd::strong_equality
, or
C
is a class type with an==
operator defined as defaulted in the definition ofC
,x == x
is well-formed when contextually converted tobool
, all ofC
's base class subobjects and non-static data members have strong structural equality, andC
has nomutable
orvolatile
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 typestd::strong_ordering
. If two pointer operandsp
andq
compare equal ([expr.eq]),p <=> q
yieldsstd::strong_ordering::equal
; ifp
andq
compare unequal,p <=> q
yieldsstd::strong_ordering::less
ifq
compares greater thanp
andstd::strong_ordering::greater
ifp
compares greater thanq
([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.
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, becausevoid
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*
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With