Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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*.

like image 285
L. F. Avatar asked May 06 '19 10:05

L. F.


People also ask

Which parameter is allowed for non-type template?

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.

Can we use non-type parameters as arguments template?

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.

Can a template be a template parameter?

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.

What is template type parameter?

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.


1 Answers

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.

like image 119
L. F. Avatar answered Oct 24 '22 09:10

L. F.