void* as template non-type parameters

132 views
Skip to first unread message

T. C.

unread,
Oct 9, 2014, 10:46:59 PM10/9/14
to std-dis...@isocpp.org
§14.1 [temp.param]/p4:

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.

Since void* isn't a pointer-to-object type, this would prohibit using them as template non-type parameters, which would disallow constructs like

template<typename T, typename std::enable_if<std::is_array<T>::value>::type* = nullptr>
void foo(T& bar) {}

(Though the above does compile in both Clang and GCC.)

Does any know if there's a reason for this or is it just a defect in the standard and the text should be "object pointer type"?

David Krauss

unread,
Oct 10, 2014, 12:47:39 AM10/10/14
to std-dis...@isocpp.org
On 2014–10–10, at 10:46 AM, T. C. <rs2...@gmail.com> wrote:

— pointer to object or pointer to function,

Since void* isn't a pointer-to-object type, this would prohibit using them as template non-type parameters, which would disallow constructs like

I think this is an editorial issue. void* is an object pointer type, which is probably what is meant.

Kazutoshi Satoda

unread,
Oct 10, 2014, 1:14:27 PM10/10/14
to std-dis...@isocpp.org
On 2014/10/10 13:47, David Krauss wrote:
> On 2014-10-10, at 10:46 AM, T. C. <rs2...@gmail.com> wrote:
>> -- pointer to object or pointer to function,
>>
>> Since void* isn't a pointer-to-object type, this would prohibit using them as template non-type parameters, which would disallow constructs like
>
> I think this is an editorial issue. void* is an object pointer type, which is probably what is meant.

To really allow void* to be a template parameter type, the restriction
in 14.3.2 [temp.arg.nontype] p5 needs to be relaxed. Now it says:
> — 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. [ Note: In particular, neither
> the null pointer conversion for a zero-valued integer literal (4.10) nor
> the derived-to-base conversion (4.10) are applied. Although 0 is a valid
> template-argument for a non-type template-parameter of integral type, it
> is not a valid template argument for a non-type template-parameter of
> pointer type. However, both (int*)0 and nullptr are valid
> template-arguments for a non-type template-parameter of type “pointer
> to int.” —end note ]
Since the template argument for pointer to object parameter shall be in
a form &id-expression with a name of an object, and T* to void* conversion
(4.10 [conv.ptr] p2) is not applied, only nullptr (or other constant
expressions of type std::nullptr_t) is valid argument for void* template
parameter.

This also means that there was no valid argument in C++03. Thus I have
thought the exclusion of void* was at least intended.

However, I think it's good to allow them if there is no solid reason to
exclude.

--
k_satoda

sean.mid...@gmail.com

unread,
Oct 10, 2014, 1:29:36 PM10/10/14
to std-dis...@isocpp.org
Is there a good use case for this? The form of enable_if shown here is non-idiomatic.

Keep in mind that template parameters must be something that can be reasonably determined at compile-time and can mangled. General pointers are not known at compile-time (the runtime linker can move things around) and there aren't necessarily clear rules on how they should be mangled on all platforms.

This is somewhat related to why strings and floats can't be template parameters, except those are far more tractable problems.

Richard Smith

unread,
Oct 10, 2014, 3:38:30 PM10/10/14
to std-dis...@isocpp.org
Speaking as the project editor: the change from 'pointer to object' to 'object pointer' is not editorial, because it changes the normative meaning of the standard in a way that doesn't reflect the clear and unambiguous intent of the committee.

Speaking as a committee member: this seems like a fairly harmless and somewhat useful change, even though the only possible template argument for a parameter of type void* would be a null pointer. I'd be in favor of allowing this, and I think this should be handled as a core issue.

Myriachan

unread,
Oct 10, 2014, 3:45:50 PM10/10/14
to std-dis...@isocpp.org, sean.mid...@gmail.com
On Friday, October 10, 2014 10:29:36 AM UTC-7, sean.mid...@gmail.com wrote:
Is there a good use case for this? The form of enable_if shown here is non-idiomatic.

Keep in mind that template parameters must be something that can be reasonably determined at compile-time and can mangled. General pointers are not known at compile-time (the runtime linker can move things around) and there aren't necessarily clear rules on how they should be mangled on all platforms.


Would it be too onerous on implementations to allow static_casting of template parameter pointers and references?  I also wonder about whether pointer arithmetic would be representable.
 
This is somewhat related to why strings and floats can't be template parameters, except those are far more tractable problems.


I wish it were legal to pass a string literal or constant-expression char * to a template taking <char...>.  It'd make compile-time string parsing really nice, and finally solve the inability to use strings as template parameters that's been confusing and/or annoying C++ programmers for 20+ years.

Floats are nasty if you have to write an emulator for the target CPU's floating point system...  (I wish constexpr allowed reading the wrong member of a union.  I don't think it's currently possible to implement std::numeric_limits<float>::infinity without compiler intrinsic help...except on GCC and MSVC 2015 which seem to be fine with reinterpret_cast or union punning in a constexpr function.)

Melissa
 

Richard Smith

unread,
Oct 10, 2014, 4:31:44 PM10/10/14
to std-dis...@isocpp.org, sean.mid...@gmail.com
On Fri, Oct 10, 2014 at 12:45 PM, Myriachan <myri...@gmail.com> wrote:
On Friday, October 10, 2014 10:29:36 AM UTC-7, sean.mid...@gmail.com wrote:
Is there a good use case for this? The form of enable_if shown here is non-idiomatic.

Keep in mind that template parameters must be something that can be reasonably determined at compile-time and can mangled. General pointers are not known at compile-time (the runtime linker can move things around) and there aren't necessarily clear rules on how they should be mangled on all platforms.


Would it be too onerous on implementations to allow static_casting of template parameter pointers and references?  I also wonder about whether pointer arithmetic would be representable.

Pointer arithmetic (and generally, pointers to subobjects) are a problem as template arguments, because of cases like:

struct X { int a, b; } x;
template<int *p> struct T { ... };
using Y = T<&x.a + 1>;
using Z = T<&x.b>;

Are Y and Z the same type? The answer "yes" is problematic because there are things you can do with a pointer to x.b that would have undefined behavior if performed on a pointer past the end of x.a. The answer "no" is problematic because they (in typical implementations) represent the same address.

Similar problems occur with pointers to union members and so on.

This is somewhat related to why strings and floats can't be template parameters, except those are far more tractable problems.


I wish it were legal to pass a string literal or constant-expression char * to a template taking <char...>.  It'd make compile-time string parsing really nice, and finally solve the inability to use strings as template parameters that's been confusing and/or annoying C++ programmers for 20+ years.

N3599 provided a way to do this. Hopefully one of its successor papers will be accepted.
 
Floats are nasty if you have to write an emulator for the target CPU's floating point system...  (I wish constexpr allowed reading the wrong member of a union.

I'm extremely happy that it doesn't. What would you expect to get if you punned between, say, a pointer and an integer? The value of a pointer is a symbolic expression during constexpr evaluation, but an integer constant represents an actual integer.
 
I don't think it's currently possible to implement std::numeric_limits<float>::infinity without compiler intrinsic help...except on GCC and MSVC 2015 which seem to be fine with reinterpret_cast or union punning in a constexpr function.)

1) The C++ standard doesn't actually say that floating-point types *have* infinities, or specify how such values would work. I think this is something we really should fix; we are far too vague about floating-point semantics at the moment.

2) C++ should probably allow reinterpret_cast'ing between same-sized integer and floating-point types; this is a commonly-desired feature, and there is no good, standard-supported way to do it right now (remember that union punning has undefined behavior, and memcpy can only transfer values between objects of the same type). Such an extension could also be supported in constant expressions, without the problems that general type punning would introduce.
 
Melissa
 
On Thursday, October 9, 2014 7:46:59 PM UTC-7, T. C. wrote:
§14.1 [temp.param]/p4:

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.

Since void* isn't a pointer-to-object type, this would prohibit using them as template non-type parameters, which would disallow constructs like

template<typename T, typename std::enable_if<std::is_array<T>::value>::type* = nullptr>
void foo(T& bar) {}

(Though the above does compile in both Clang and GCC.)

Does any know if there's a reason for this or is it just a defect in the standard and the text should be "object pointer type"?

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

Gabriel Dos Reis

unread,
Oct 11, 2014, 1:29:11 PM10/11/14
to std-dis...@isocpp.org
Well, non-type template arguments aren't any constant expressions -- if
you look at them carefully, you would notice that they are a subset of
the usual "constant expressions" (this is not just because of constexpr).
This is because, constants are kept in symbolic forms at compile time and
there is no good way in general to guarantee a unique normal form (which
you have at runtime) when you allow the fullset of constant expressions
and 'terminal types' such as 'void*'.

-- Gaby

Gabriel Dos Reis

unread,
Oct 11, 2014, 1:34:45 PM10/11/14
to std-dis...@isocpp.org, sean.mid...@gmail.com
Richard Smith <ric...@metafoo.co.uk> writes:

[...]

| Floats are nasty if you have to write an emulator for the target
| CPU's floating point system...  (I wish constexpr allowed reading
| the wrong member of a union.
|
|
| I'm extremely happy that it doesn't. What would you expect to get if
| you punned between, say, a pointer and an integer?

Turn your compiler into a potent virus? :-)

| The value of a pointer is a symbolic expression during constexpr
| evaluation, but an integer constant represents an actual integer.

It depends on the implementation!
If I remember GCC's implementation correctly, an integer constant is
also represented as a data structure that is more than just a intmax_t.

[...]

| 2) C++ should probably allow reinterpret_cast'ing between same-sized
| integer and floating-point types;

At best "conditionally-supported", but certainly not a requirment
imposed on every implementation.

| this is a commonly-desired feature,
| and there is no good, standard-supported way to do it right now
| (remember that union punning has undefined behavior, and memcpy can
| only transfer values between objects of the same type). Such an
| extension could also be supported in constant expressions, without the
| problems that general type punning would introduce.

-- Gaby
Reply all
Reply to author
Forward
0 new messages