Reputation: 39
I'm trying to understand C++11's universal references, and wrote the following code:
#include <cstdio>
template <typename L, typename R>
static void Run(L&& lhs, const R& rhs) {
lhs += rhs;
}
static void Run2(int&& lhs, const int& rhs) {
lhs += rhs;
}
int main() {
int a = 0;
int b = 3;
Run(a, b);
printf("%d\n", a);
// This does not compile.
Run2(a, b);
printf("%d\n", a);
return 0;
}
Note that Run()
works, but the line calling Run2()
will fail to compile. I can't really figure out why. The error I get is no known conversion from 'int' to 'int &&' for 1st argument
.
I'm sure the compiler is right, but I can't figure out under why. It seems to be Run()
and Run2()
are doing the same things right?
Btw, changing Run2()
to be templated with a single parameter also does not work.
Upvotes: 0
Views: 460
Reputation: 2605
"Universal references" can only happen in contexts where the type of the reference is deduced, such as in a template type context T&&
. In this case, T can either be deduced as int
for an rvalue argument, and so you have int&&
as the argument type; or as int&
for your lvalue argument, which would give the type int& &&
that per reference collapsing rules becomes just int&
. In your Run2 function, the type is directly int&&
and so it cannot bind to any lvalue unless you use std::move
.
Upvotes: 5
Reputation: 4429
Run(a,b)
calls a function where typename L==int& and R==int
so
static void Run(L&& lhs, const R& hrs)
is template instantiated as
static void Run(int & && lhs, const int& hrs)
or actually because of reference collapsing, it becomes
static void Run(int& lhs, const int& hrs)
Run2
expects an rvalue reference for lhs
which it can only get from casting or being given an unnamed variable (temporary). In, your case, you could call the function using a cast:
Run(std::move(a), b)
Upvotes: 1
Reputation: 6476
The first one is a universal reference (as it described in the book of Scott Meyers Effective Modern C++) and the second one is rvalue reference
In order to make it work you need to call it like this:
Run2(std::move(a), b);
Upvotes: 1
Reputation: 477150
Rvalue references cannot bind to lvalues.
The special rule for creating forwarding references is that a deduced template parameter T
may be deduced as T = U&
when it appears in a function parameter T&&
, which makes the function parameter type be U&
(thanks to reference collapsing from U& &&
). However, the types of the parameters of Run2
are not deduced, so the first parameter is directly an rvalue reference.
Changing Run2
to a function template with a single template parameter means that the argument deduction will fail, since there is no consistent set of types that fits both arguments.
Upvotes: 2