Search code examples
c++templatesoverloading

Making function with template and without gives different outcomes?


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?


Solution

  • 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.