apriori
apriori

Reputation: 1262

Void pointer as template argument in C++

The following does not compile:

template<void *p>
class X {
// ...
};

int r;

int main()
{
    X<&r> x;

    return 0;
}

The error message is

x.cc:10:6: error: could not convert template argument ‘& r’ to ‘void*’

Explicitly casting &r to (void *) doesn't help either. The error message becomes:

x.cc:10:14: error: could not convert template argument ‘(void*)(& r)’ to ‘void*’

Which part of the standard specifies that behaviour? The GCC version is gcc version 5.2.1 20151003 (Ubuntu 5.2.1-21ubuntu2)

Edit:

Please note that using e.g. int * instead of void * works as expected.

Edit: (answering myself)

It does not work with gcc HEAD 6.0.0 20151016 (experimental) when specifying -std=c++1z, neither with implicit nor with explicit casting to "void *".

It does work with clang HEAD 3.8.0 (trunk 250513) and has been since (at least) clang 3.6.0 (tags/RELEASE_360/final) when specifying --std=c++1z and explicitly casting to *void *". Without the explicit cast, clang complains as follows:

x.cc:10:7: error: conversion from 'int *' to 'void *' is not allowed in a converted constant expression

Responsible for fixing this bug in the c++ language specification is N4268 which clang already implements.

Upvotes: 11

Views: 2868

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69892

I can't quote you the chapter and verse off the top of my head (edits are welcome), but what you are trying to do is not allowed in c++.

Template parameters must be known at compile time. Pointers are only resolved at link time unless:

  1. they are defaulted using = nullptr in the template argument list.

  2. they are member function pointers (which are known at compile time since they are merely offsets).

for example, this will compile:

template<void * = nullptr>
class X {
    // ...
};

int r;

int main()
{
    X<nullptr> x;

    return 0;
}

Upvotes: 1

user5441780
user5441780

Reputation: 11

Normally, there is a conversion allowed for any pointer to void*.

[C++11, 4.10/2] A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.

However, for non-type template arguments, certain conversions are specified:

[C++11, 14.3.2/5] The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.

[...]

— for a non-type template-parameter of type pointer to object, qualification conversions (4.4) and the array-to-pointer conversion (4.2) are applied; if the template-argument is of type std::nullptr_t, the null pointer conversion (4.10) is applied. [...]

By omission, we can reason this conversion is simply not allowed.

Upvotes: 1

Related Questions