I'm reading about perfect forwarding, and this is something I've learnt that has confused me:
When you're trying to achieve perfect forwarding, you'll do something like this:
template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T &&x); // like this: void foo(int& &&x)
So then I thought, wait, does that mean that if I did this:
template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T x); // like this: void foo(int& x);
But that's not what happens. foo instead looks like this: void foo(int x);
My question: How come in the perfect forwarding function, T turns into a T& or T&&, but in the other one, T isn't a reference? Can somebody tell me the exact rules for this? I need some clarification!
A template parameter T can be deduced as a reference type only if it appears in a function parameter of the form T&&
A function template of the form:
template<class T> void f(T x)T as an object type (and x is an object type so is passed by value)template<class T> void f(T& x)T as an object type (and then x has lvalue reference type)template<class T> void f(T&& x)T as
x has lvalue reference type due to reference collapsing rules)x has rvalue reference type)How come in the perfect forwarding function, T turns into a T& or T&&, [...]
This is wrong. T becomes a reference type L& or an object type R, not a reference R&&.
The function parameter of the form T&& thus becomes
L& (because adding an rvalue reference to an lvalue reference is still an lvalue reference, just like add_rvalue_reference<L&>::type is still L&)R&& (because add_rvalue_reference<R>::type is R&&)This is because of the way type deduction is defined, and it is only related to perfect forwarding in the sense that the result of std::forward<>() is an rvalue if an rvalue reference is passed, and an lvalue if an lvalue reference is passed.
But in general, when you do not have a reference to begin with, your T is not going to be deduced as a reference type (i.e. as A&, whatever A could be). If that was the case, as Yakk correctly points out in the comments, it would be impossible to write a function template that takes its arguments by value.
In particular, the reference collapsing rule you are referring to is defined in Paragraph 14.8.2.1/4 of the C++11 Standard:
If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. [ Example:
template <class T> int f(T&&); template <class T> int g(const T&&); int i; int n1 = f(i); // calls f<int&>(int&) int n2 = f(0); // calls f<int>(int&&) int n3 = g(i); // error: would call g<int>(const int&&), which // would bind an rvalue reference to an lvalue—end example ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With