Ignore rvalue-references for a second, and instead pretend this was allowed:
void modify_int(int& i)
{
i = 1;
}
void foo(int& x)
{
modify_int(x); // okay, modify_int references x
}
int i = 7;
foo(i); // makes i = 1
// illegal in standard C++, cannot bind a temporary to a non-const reference
foo(5); // makes the temporary integer equal to 1
You can see that the temporary object gets modified, which is perfectly fine. However, this binding was made illegal in C++ because its usually not desired (it reads as if 5 was being changed to 1, after all).
All rvalue-references do is enable the binding of temporary values to references, but safely because we understand that we're dealing with a value that should be considered temporary:
void modify_int(int& i)
{
i = 1;
}
void foo(int&& x)
{
modify_int(x); // okay, modify_int references x
}
int i = 7;
foo(std::move(i)); // makes i = 1 (std::move makes it an rvalue)
// legal in C++11, temporary is bound to rvalue-reference
foo(5); // makes the temporary integer equal to 1
Note that in this version of foo
, passing to modify_int
is still perfectly fine. Once inside the function, the fact that it was an rvalue-reference instead of an lvalue-reference is irrelevant: we still have an object to refer to. Forwarding is used in templates to preserve the value category:
void test(int& i) {} // lvalue version of test
void test(int&& i) {} // rvalue version of test
template <typename T>
void foo(T&& x)
{
// if x was an lvalue, forward does nothing;
// if x was an rvalue, forward std::move's it
test(std::forward<T>(x));
}
int i = 7;
foo(i); // calls lvalue version of test
foo(5); // calls rvalue version of test
Your code without forwarding is similar to the second snippet in my answer. Once inside the factory
function, a1
is just a regular lvalue, and binds to the constructor reference just fine. But with forwarding, it turns back into an rvalue (because factory(5)
calls it with an rvalue), which cannot bind to the lvalue-reference, resulting in an error.