When working to non-superficial depth with forwarding references(1), it's important to understand how they actually work and not treat them as "magic."
All the "magic" of forwarding references is this ruling in the standard:
When type deduction happens for parameter type T &&
where T
is a deduced type, and an lvalue of type U
is used as the argument, the type U &
is used for type deduction instead of U
.
Let's take this example:
template <class T>
void foo(T&& a);
int i = 42;
What happens when you call foo(42)
: 42
is an rvalue of type int
. Therefore, T
will be deduced to int
and the type of a
will therefore be int &&
, an rvalue reference to int
.
What happens when you call foo(i)
: i
is an lvalue of type int
. Because of the forwarding reference rules, deduction will happen as if int &
was the type. T
is therefore deduced to int &
; the type of a
is therefore "int & &&
", which reference-collapses to int &
.
With this in mind, it's clear why your example fails. The same T
would have to be deduced as both int &
(because of n
) and as int
(because of std::move(n)
). So naturally, deduction fails.
If you want each of the parameters to be a forwarding reference on its own, you need a separate template parameter for each:
template<class T, class U>
void f(T&& a, U&& b)
{}
(1) Notice that as per the proposal N4164, which has been accepted into the working paper, the preferred term is forwarding references.