I wrote this code:
#include <iostream>
void test(std::string && x) {
std::cout << "test: 1" << std::endl;
}
void test(const std::string & x) {
std::cout << "test: 2" << std::endl;
}
template<typename T>
void test2(T&& x) {
std::cout << "test2: 1" << std::endl;
}
template<typename T>
void test2(const T & x) {
std::cout << "test2: 2" << std::endl;
}
int main()
{
std::string x = "aaa";
std::string & y = x;
test(y);
test2(y);
return 0;
}
It has 4 functions, two named test
and two test2
. test2
have argument choosen by template and test
has it static. Both types have one overload for const T & x
and one for T && x
. But if i call them with T &
, different type is called. Result of code above is:
test: 2
test2: 1
How, and more importantly why, is this happening?
Roughly speaking, the compiler prefers function overloads that involve "easier" type conversions.
Since you can't convert an lvalue reference to an rvalue reference, the function void test(std::string &&x);
can't be called at all, but the conversion from std::string&
to const std::string&
is okay. That's the reason why you get "2" when calling test
.
The template case is different because it also involves type deduction.
The T&& x
is a "universal reference", which is able to bind to anything, and so it doesn't involve any type conversions. i.e. when T
is deduced, the function test2(T &&x)
becomes test2(std::string &x)
, which matches perfectly and requires no type conversions.
On the other hand test2(const T& x)
becomes test2(const std::string &x)
, which requires 1 type conversion, so it's less favorable than the first overload.