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

Posted

技术标签:

【中文标题】C++17和C++11中的非类型模板参数有啥区别?【英文标题】:What is the difference between non-type template parameters in C++17 and C++11?C++17和C++11中的非类型模板参数有什么区别? 【发布时间】:2021-03-12 15:00:59 【问题描述】:

考虑这段代码:

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) 的类型。

唯一的区别是:

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

【问题讨论】:

constexpr auto function = &amp;something;会有什么变化吗? @BenVoigt,不,没有任何改变。 但是如果您将&amp;something 直接放入调用do_something&lt;&amp;function&gt;(10, 20) 那么一切都很好吗? 请注意,您在 int something(int first, int second) 中缺少返回语句 @BenVoigt,我尝试了do_something&lt;&amp;function&gt;(1, 1);... function = &amp;something; do_something&lt;&amp;function&gt;(1, 1);,但没有任何改变。 【参考方案1】:

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

C++11:

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

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

C++17:

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

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

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

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

【讨论】:

是否可以在不消除变量的情况下通过引用函数来解决“现在你有一个指向函数的指针”,然后在模板参数列表中应用&amp; 并命名引用( &amp; 自然适用于所指对象)?【参考方案2】:

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

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

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

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

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

【讨论】:

以上是关于C++17和C++11中的非类型模板参数有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

C++20:非类型模板参数中的非捕获 lambda

c++11-17 模板核心知识—— 理解模板参数推导规则

GCC C++14/17 成员函数指针模板参数的区别

C语言中的.c和.cpp有啥区别?

java中println和printf有啥区别

c++11 为什么使用ref,和引用的区别