I want to replace writing function signature with decltype, and found out it does not compile with most compilers. Is it a new feature or maybe unspecified behaviour?
#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();
}
so (not really sure about compiler versions):
clang - 3.4 http://rextester.com/UOIV91915
compiles, runs, produces output
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 - update 4
fatal error C1001: An internal error has occurred in the compiler.
Non-type template parameters cannot have function type. They can have pointer to function type, and there is a paragraph in the standard that somehow implies that your code is correct - [temp.param]/8:
A non-type template-parameter of type “array of
T
” or “function returningT
” is adjusted to be of type “pointer toT
” or “pointer to function returningT
”, respectively.
However, it is not clear whether this is done after template argument substitution or before it, which is covered in this defect report. An easy fix is to simply write
using df = decltype(&f);
Demo.
using df = decltype((f));
work?[dcl.type.simple]/4:
For an expression
e
, the type denoted bydecltype(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 bye
. 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)
isT&&
, whereT
is the type ofe
;- otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;- otherwise,
decltype(e)
is the type ofe
.
(f)
is parenthesized and an lvalue, thus decltype((f))
is an lvalue reference to f
's function type - void(&)(int)
. Template parameters can have reference to function type, thus it works. However, as this fact is quite counterintuitive (and not so well known), decltype(&f)
should be less irritative in your code.