C++17 和 C++11 中的非类型模板参数有什么区别?

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

考虑这段代码:

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);
}

使用 C++17 标准兼容编译器编译并 运行,但使用 C++11 标准失败:

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

C++11非类型模板参数和C++17非类型模板参数有什么区别?在 §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.

并且在 §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).

唯一的区别是:

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

我认为这与我的问题无关,因为占位符类型类似于 auto,我向模板发送的值不是占位符类型或类型。

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

换句话说,在 C++11 中,非类型模板参数在指针的情况下可能采用的形式受到严格限制。您可以直接命名指向的实体,如 &something,或者,由于这是一个函数,您可以省略 & 并允许进行函数到指针的转换,但您不能使用包含指针值的对象的名称,即使它是 constexpr 对象。

在 C++17 中,几乎所有此类限制都被删除了。特别是,函数指针类型的非类型模板参数的模板参数可以是适当函数指针类型的任何转换常量表达式。

相关差异在于 [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.

在 C++11 中,模板参数 function 不是 & id 表达式,并且名称未引用函数 something。它引用了一个int (*const)(int, int)类型的变量,其值指向something。 (而且 do_something<&function> 无济于事,因为现在你有一个指向函数指针的指针,它不会转换为指向函数类型的指针。)

在 C++17 中,语法要求消失了,限制是对不能指向或引用的对象的更宽松的纯语义要求。