SwiftMango
SwiftMango

Reputation: 15284

Why void* as template parameter works as function parameter but not template parameter?

I have two version of my_begin:

template<typename T, typename std::enable_if<std::is_array<T>::value>::type* = 0>
typename std::decay<T>::type my_begin(T& array) {
    return array;
}

and

template<typename T>
typename std::decay<T>::type my_begin(T& array, 
        typename std::enable_if<std::is_array<T>::value>::type* = 0) {
    return array;
}

However the first one does not work and gives error:

int a[10];
int* a_it = my_begin(a);

error:

main.cpp:17:30: note:   template argument deduction/substitution failed:

main.cpp:16:80: error: could not convert template argument '0' to 'std::enable_if<true, void>::type* {aka void*}'

 template<typename T, typename std::enable_if<std::is_array<T>::value>::type* = 0>

But the second one works. When I change 0 in the first one to nullptr, it works too (but still not working for NULL). I do understand that in template it requires explicit casting (in this case, from int to void*, but why the second one does not require it?

Another question, if I remove the whitespace between * and =, it also failed. Why is that?

Upvotes: 3

Views: 688

Answers (1)

T.C.
T.C.

Reputation: 137315

§14.1 [temp.param]/p4 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.

Read literally, this disallows void* template parameters altogether. void* is an object pointer type but isn't a pointer to object type (§3.9.2 [basic.compound]/p3):

The type of a pointer to void or a pointer to an object type is called an object pointer type. [ Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. —end note ]

If we assume it's a defect and that the standard really meant to say "object pointer type", then using 0 and company is still disallowed by §14.3.2 [temp.arg.nontype]/p5 (emphasis added):

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. [ 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 ]

= 0 works for function default arguments because those are subject to the normal conversion rules, which allows an integer literal with value 0 to convert to a null pointer, rather than the special rules for template arguments.


if I remove the whitespace between * and =, it also failed. Why is that?

Maximum munch. If the whitespace is removed, *= is a single token (the compound assignment operator). Just like in C++03 when you had to put a space between the >s in std::vector<std::vector<int> >.

Upvotes: 9

Related Questions