I am using a decltype
to ensure a template function force2
has the same parameter and return types as another template function force1
but it doesn't compile. I require the functions to be templated.
Consider this code:
template <typename N>
void force1(N a);
template <typename N>
using ForceType = decltype(force1<N>);
template <typename N>
ForceType<N> force2;
template <typename N>
void force2(N a)
{}
This results in an error:
<source>:11:16: error: 'template<class N> void force2(N)' conflicts with a previous declaration
11 | void force2(N a)
| ^
<source>:8:14: note: previous declaration 'template<class N> ForceType<N> force2<N>'
8 | ForceType<N> force2;
| ^~~~~~
Compiler returned: 1
I expected this to compile: with force2
being a templated function with empty body. What is actually going on over here? The compiler hints that template<class N> void force2(N)
is not the same as template<class N> ForceType<N> force2<N>
which I am not able to understand. They definitely seem different on the first look but shouldn't the expansion of ForceType<N>
result in template<class N> void force2(N)
?
When I use a concrete type in declaration of force2
:
template <typename N>
void force1(N a);
template <typename N>
using ForceType = decltype(force1<N>);
ForceType<int> force2;
void force2(int a)
{}
It compiles without errors.
ForceType<N>
is a dependent type.
If a function declaration acquired its function type through a dependent type without using the syntactic form of a function declarator, the program is ill-formed.
ForceType<N> force2;
does not meet this requirement, so this is ill-formed.
In compilers, this will be parsed as a variable template since, because of the above rule, it cannot be a function template.
Any attempt to actually use force2<N>
will result in an error (https://godbolt.org/z/f3Ke3x8db)
And even if you were to fix it by making it of the form of a function declaration:
template<typename>
struct unary_fn;
template<typename Ret, typename Arg>
struct unary_fn<Ret(Arg)> { using return_type = Ret; using argument_type = Arg; };
template <typename N>
void force1(N a);
template <typename N>
using ForceType = typename unary_fn<decltype(force1<N>)>::return_type(typename unary_fn<decltype(force1<N>)>::argument_type);
template <typename N>
ForceType<N> force2;
template <typename N>
void force2(N a)
{}
This would declare two separate templates overloads because ForceType<N>
and void(N)
are not equivalent (i.e., the latter is not a definition of the former)