I've been trying to understand the way C++ selects templates or member functions. Consider the following code sample:
#include <iostream>
#include <string>
struct Test
{
template<typename X>
explicit Test( X&& s){ std::cout << "1" << std::endl;}
explicit Test( const std::string& s) { std::cout << "2"<< std::endl; }
explicit Test( std::string&& s) { std::cout << "3"<< std::endl; }
};
int main ()
{
std::string line = "TEST";
Test test( line );
}
I'm getting "1" printed on console. Why "2" is not selected if it is a non-template that matches the parameter types?
The constructor template's parameter is declared as forwarding reference. When being passed an lvalue such as line
, the template parameter X
is deduced as std::string&
, and after reference collapsing the parameter type is std::string&
, i.e. an lvalue-reference to non-const std::string
. It's an exact match and wins in overload resolution.
On the other hand, the constructor taking const std::string&
requires const-qualifying the argument, and the constructor taking std::string&&
can't be used with lvalues.
The reason why your templated constructor is selected instead of the non-templated ones has already been explained in this other answer.
However, note that you can still rely on SFINAE for disabling your templated constructor for std::string
arguments:
#include <type_traits>
template<typename T>
constexpr bool is_string_v = std::is_same_v<std::decay_t<T>, std::string>;
Then, in your Test
class, add an unnamed template parameter to your templated constructor:
struct Test
{
template<typename X, typename = std::enable_if_t<!is_string_v<X>>>
explicit Test( X&& s){ std::cout << "1" << std::endl;}
explicit Test( const std::string& s) { std::cout << "2"<< std::endl; }
explicit Test( std::string&& s) { std::cout << "3"<< std::endl; }
};
This way:
int main ()
{
std::string line;
Test test(line); // selects 2
const std::string cStr;
Test testStr(cStr); // selects 2
int i{};
Test testInt(i); // selects 1
float f{};
Test testFloat(f); // selects 1
}
With C++20 concepts, you can very likely end up writing something more readable and expressive.
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