What is the difference between non-type template parameters in C++17 and C++11?

300 Views Asked by At

Consider this code:

using func = int (*)(int, int);

template<func F>
void do_something(int first, int second) {}

int something(int first, int second) { return 42; }

void  f()
{
  constexpr auto  function = something;
  do_something<function>(10, 20);
}

Which is compiled and run with C++17 standard compatible compiler, but it's fail with C++11 standard:

 error: no matching function for call to ‘do_something<function>(int, int)’
   17 |   do_something<function>(10, 20);

What is the difference between C++11 non-type template parameters and C++17 non-type template parameters? in §14.1.4 [temp.param][n3690]:

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.

And in §17.1.4 [temp.param][n4713]:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
(4.1) — integral or enumeration type,
(4.2) — pointer to object or pointer to function,
(4.3) — lvalue reference to object or lvalue reference to function,
(4.4) — pointer to member,
(4.5) — std::nullptr_t, or
(4.6) — a type that contains a placeholder type (10.1.7.4).

The only difference is:

< — a type that contains a placeholder type (10.1.7.4).

Which I don't think is related to my question because a placeholder type is something like auto, and I sent a value to template not a placeholder type or a type.

2

There are 2 best solutions below

1
On BEST ANSWER

The relevant difference is in the requirements on allowed template arguments (not template parameters) in [temp.arg.nontype].

C++11:

A template-argument for a non-type, non-template template-parameter shall be one of:

  • ...
  • a constant expression that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  • ...

C++17:

A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject,
  • a temporary object,
  • a string literal,
  • the result of a typeid expression, or
  • a predefined __func__ variable.

In C++11, the template-argument function is not in the form & id-expression, and the name does not refer to the function something. It refers to a variable of type int (*const)(int, int), whose value points at something. (And do_something<&function> wouldn't help, because now you have a pointer to pointer to function, which won't convert to the pointer to function type.)

In C++17, the syntax requirement is gone, and the restriction is a more relaxed purely semantic requirement on what objects can't be pointed at or referenced.

0
On

C++11 [temp.arg.nontype]/1:

A template-argument for a non-type, non-template template-parameter shall be one of:

  • [...]
  • the name of a non-type template-parameter; or
  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  • [...]

In other words, the form that a non-type template argument may take in C++11, in the case of a pointer, is heavily restricted. You can directly name the entity pointed to, as in &something, or, since this is a function, you may omit the & and allow the function-to-pointer conversion to take place, but you cannot use the name of an object that contains the pointer value, even if it is a constexpr object.

In C++17, almost all restrictions of this type were removed. In particular, a template argument for a non-type template parameter of function pointer type can be any converted constant expression of the appropriate function pointer type.