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

mar*_*inj 3 c++ templates decltype language-lawyer

我想用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();
}
Run Code Online (Sandbox Code Playgroud)

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

clang - 3.4 http://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();
Run Code Online (Sandbox Code Playgroud)

Visual Studio 2013 - 更新4

fatal error C1001: An internal error has occurred in the compiler.
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 5

非类型模板参数不能具有函数类型.它们可以有指向函数类型的指针,标准中有一个段落暗示你的代码是正确的 - [temp.param]/8:

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

但是,目前尚不清楚这是在模板参数替换之后还是之前完成,这在此缺陷报告中有所涉及.一个简单的解决方法是简单地写

using df = decltype(&f);
Run Code Online (Sandbox Code Playgroud)

演示.


为什么using df = decltype((f));工作?

[dcl.type.simple]/4:

对于表达式e,表示的类型decltype(e)定义如下:

  • if e是未加密码的id-expression或未加括号的类成员访问(5.2.5),decltype(e)是名为的实体的类型e.如果没有这样的实体,或者如果e命名了一组重载函数,那么该程序就会形成错误;
  • 否则,如果e是xvalue,decltype(e)则是T&&,其中T的类型是e;
  • 否则,如果e是左值,decltype(e)则是T&,其中T的类型e;
  • 否则,decltype(e)是的类型e.

(f)括号和左值,因此decltype((f))是对f函数类型的左值引用void(&)(int).模板参数可以引用函数类型,因此它可以工作.但是,由于这个事实非常违反直觉(而且不是很清楚),decltype(&f)因此在代码中应该减少刺激性.