While looking at the reference pages for std::forward I came across something odd. The example is passing an lvalue as an rvalue reference.. but to a global function... and it compiles and runs. I tried the same thing as a member function and it fails to compile. What gives? I would expect both calls to fail without the use of std::move
or std::forward<T>
.
#include <iostream>
template <typename T>
void globalDoSomething(T &&data) {
std::cout << "We're doing it!!" << std::endl;
}
template <typename T>
class A {
public:
void doSomething(T &&data);
};
template <typename T>
void A<T>::doSomething(T &&data)
{
std::cerr << "Ah, man. I won't compile." << std::endl;
}
template class A<int>;
int main()
{
int b = 0;
globalDoSomething(b);
A<int> a;
a.doSomething(b);
return 0;
}
It's because the automatic template deduction for globalDoSomething
infers T
as int&
.
If you explicitly instantiate the template function with globalDoSomething<int>(b);
like you did for the member function of the template class, it will also fail to compile.
Conversely, if you instantiate the template class with A<int&> a;
, it will successfully compile.
To build/add on @Patrick Roberts answer, from template argument deduction
If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction
In short, while b
is an lvalue, it infers T
as int&
indeed. And as shown in the example
template<class T>
int f(T&& x) { // x is a forwarding reference
return g(std::forward<T>(x)); // and so can be forwarded
}
int main() {
int i;
f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue
}
To test this, you can set a few overloaded functions with different types and check
#include <iostream>
#include <utility>
void f(int&& x) {
std::cout << "rvalue reference overload f(" ")\n";
}
void f(const int& x) {
std::cout << "lvalue reference to const overload f(" ")\n";
}
void f(int& x) {
std::cout << "lvalue reference overload f(" ")\n";
}
template <typename T>
void globalDoSomething(T &&data) {
f(data);
}
int main()
{
int b = 0;
globalDoSomething(b);
return 0;
}
And the output is
lvalue reference overload f()
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