首页 > 解决方案 > C++17和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] 中:

非类型模板参数应具有以下类型之一(可选 cv 限定):
— 整数或枚举类型,
— 指向对象的指针或指向函数的指针,
— 对对象的左值引用或对函数的左值引用,
— 指向成员,
——std::nullptr_t。

在 §17.1.4 [temp.param][n4713] 中:

非类型模板参数应具有以下类型之一(可选 cv 限定):
(4.1) — 整数或枚举类型,
(4.2) — 指向对象或指向函数的指针,
(4.3) — 对对象的左值引用或对函数的左值引用,
(4.4) — 指向成员的指针,
(4.5) — std::nullptr_t,或
(4.6) — 包含占位符类型的类型 (10.1.7.4)。

唯一的区别是:

< — 包含占位符类型 (10.1.7.4) 的类型。

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

标签: c++c++11c++17language-lawyernon-type

解决方案


相关的区别在于 [temp.arg.nontype] 中对允许的模板参数(不是模板参数)的要求。

C++11:

非类型、非模板模板参数模板参数应为以下之一:

  • ...
  • 一个常量表达式,指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数template-id,但不包括非静态类成员,表示(忽略括号)与& id-expression一样,但&如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或者
  • ...

C++17

非类型模板参数的模板参数是模板参数类型的转换常量表达式。对于引用或指针类型的非类型模板参数,常量表达式的值不应引用(或对于指针类型,不应是地址):

  • 一个子对象,
  • 一个临时对象,
  • 字符串文字,
  • typeid表达式的结果,或
  • 一个预定义的__func__变量。

在 C++11 中,template-argument function不是& id-expression的形式,名称也不是指 function something。它指的是一个类型为 的变量int (*const)(int, int),其值指向something。(do_something<&function>也无济于事,因为现在你有一个指向函数的指针,它不会转换为指向函数类型的指针。)

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


推荐阅读