Using decltype for templated function declaration results in "conflict" when defining the templated function

112 views Asked by At

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.

2

There are 2 answers

10
Artyer On BEST ANSWER

ForceType<N> is a dependent type.

[temp.spec.general]p8:

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)

0
vince167861 On

In the using ForceType template declaration, decltype(force1<N>) is void(N a). So ForceType<int> force2; is the same as declaring void force2(int a), which is simply a function, not a function template.

Now, in template <typename N> void force2(N a) {}, you are trying to declare a template with the name force2, which is already "used" by the previous declaration.

Note that in the second example, void force2(int a) {} is the definition of the function, not conflicting with the force2 declaration.