Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String literal matches bool overload instead of std::string

I am trying to write a C++ class that has some overloaded methods:

class Output
{
public:
    static void Print(bool value)
    {
        std::cout << value ? "True" : "False";
    }

    static void Print(std::string value)
    {
        std::cout << value;
    }
};

Now lets say I call the method as follows:

Output::Print("Hello World");

this is the result

True

So, why, when I have defined that the method can accept boolean and string, does it use the boolean overload when I pass in a non-boolean value?

EDIT: I come from a C#/Java environment, so quite new to C++!

like image 862
Matthew Layton Avatar asked Feb 08 '13 10:02

Matthew Layton


4 Answers

"Hello World" is a string literal of type "array of 12 const char" which can be converted to a "pointer to const char" which can in turn be converted to a bool. That's precisely what is happening. The compiler prefers this to using std::string's conversion constructor.

A conversion sequence involving a conversion constructor is known as a user-defined conversion sequence. The conversion from "Hello World" to a bool is a standard conversion sequence. The standard states that a standard conversion sequence is always better than a user-defined conversion sequence (§13.3.3.2/2):

a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence

This "better conversion sequence" analysis is done for each argument of each viable function (and you only have one argument) and the better function is chosen by overload resolution.

If you want to make sure the std::string version is called, you need to give it an std::string:

Output::Print(std::string("Hello World"));
like image 172
Joseph Mansfield Avatar answered Sep 30 '22 03:09

Joseph Mansfield


Not sure why nobody posted this, but you can add another overload that converts from const char* to std::string for you. This saves the caller from having to worry about this.

class Output
{
public:
    static void Print(bool value)
    {
        std::cout << value ? "True" : "False";
    }

    static void Print(std::string value)
    {
        std::cout << value;
    }

    // Just add the override that cast to std::string
    static void Print(const char* value)
    {
        Output::Print(std::string(value));
    }
};
like image 34
Zachary Canann Avatar answered Oct 01 '22 03:10

Zachary Canann


FWIW, it can be addressed this way (if templates can be used), if you don't want to add overloads for const char*.

#include <iostream>
#include <string>
#include <type_traits>

template <typename Bool,
          typename T = std::enable_if_t<std::is_same<Bool, bool>{}>>
void foo(Bool)
{
  std::cerr << "bool\n";
}

void foo(const std::string&)
{
  std::cerr << "string\n";  
}

int main()
{
  foo("bar");
  foo(false);
}
like image 23
akim Avatar answered Oct 01 '22 03:10

akim


Since C++14 we have the operator""s from the std::string_literals namespace, which can be used to tell the compiler to bind to the string (or string_view in C++17) overload:

using namespace std::string_literals;
Output::Print("Hello World"s);

Prints: Hello World

like image 22
Замфир Йончев Avatar answered Oct 04 '22 03:10

Замфир Йончев