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

nontype template parameter produced with decltype for function

我想用 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.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();

Visual Studio 2013 - 更新 4

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

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

A non-type template-parameter of type “array of T” or “function returning T” is adjusted to be of type “pointer to T” or “pointer to function returning T”, respectively.

但是,尚不清楚这是在模板参数替换之后还是之前完成的,后者已涵盖 in this defect report。一个简单的解决方法是简单地写

using df = decltype(&f);

Demo.


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

[dcl.type.simple]/4:

For an expression e, the type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  • otherwise, decltype(e) is the type of e.

(f) 是带括号的左值,因此 decltype((f)) 是对 f 的函数类型 - void(&)(int) 的左值引用。模板参数可以引用函数类型,因此它可以工作。然而,由于这个事实非常违反直觉(而且不太为人所知),decltype(&f) 在您的代码中应该不那么令人恼火。