使用 decltype for 函数生成的非类型模板参数

nontype template parameter produced with decltype for function

本文关键字:类型 参数 decltype for 函数 使用      更新时间:2023-10-16

我想用decltype替换编写函数签名,发现它不能用大多数编译器编译。这是一个新功能还是未指定的行为?

#include <iostream>
template <typename T, T nontype>
struct CL {
    void call() { nontype(123); }
};
void f(int n) {
    std::cout << n << std::endl;
}
CL<void(*)(int), f> cl7;
using df = decltype(f);
CL<df, f> cl8; // << error
int main() {
    cl8.call();
}

所以(不太确定编译器版本):

叮当 - 3.4http://rextester.com/UOIV91915

编译、运行、生成输出

g++ 4.9+http://coliru.stacked-crooked.com/a/dbec1e202c48fd81

main.cpp:14:9: error: 'void(int)' is not a valid type for a template non-type parameter
 CL<df, f> cl8; // << error
         ^
main.cpp:14:14: error: invalid type in declaration before ';' token
 CL<df, f> cl8; // << error
              ^
main.cpp: In function 'int main()':
main.cpp:17:6: error: request for member 'call' in 'cl8', which is of non-class type 'int'
  cl8.call();

Visual Studio 2013 - 更新 4

fatal error C1001: An internal error has occurred in the compiler.

非类型模板参数不能具有函数类型。它们可以有指向函数类型的指针,并且标准中有一段以某种方式暗示您的代码是正确的 - [temp.param]/8:

类型为"T数组"或"函数"的非类型模板参数 返回T"调整为"指向T的指针"或"指向的指针" 函数分别返回T"。

但是,目前尚不清楚这是在模板参数替换

之后还是在模板参数替换之前完成的,这在本缺陷报告中有所介绍。一个简单的解决方法是简单地编写

using df = decltype(&f);

演示


为什么using df = decltype((f));有效?

[dcl.type.simple]/4:

对于表达式e,用 decltype(e) 表示的类型定义为 遵循:

  • 如果e无括号的 ID 表达式或无括号的类成员访问 (5.2.5),则decltype(e)是实体的类型 由e命名。如果没有这样的实体,或者如果 e 命名一组 函数重载,程序格式不正确;
  • 否则,如果e是x值,则decltype(e) T&&,其中Te的类型;
  • 否则,如果e是左值,则decltype(e) T&,其中Te的类型;
  • 否则,decltype(e)e的类型。

(f)是括号和左值,因此decltype((f))是对f函数类型-void(&)(int)的左值引用。模板参数可以引用函数类型,因此它可以工作。但是,由于这一事实非常违反直觉(并且不太为人所知),因此decltype(&f)代码中的刺激性应该不那么大。