53

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++!

M.M
  • 138,810
  • 21
  • 208
  • 365
Matthew Layton
  • 39,871
  • 52
  • 185
  • 313
  • 1
    @meh, because they are not instance functions. – Matthew Layton Feb 08 '13 at 10:18
  • 2
    your `const char*` is a native-type promotion to bool and a constructed value type promotion to `std::string`. Which would *you* choose. ? Now guess which one the *compiler* chose.. – WhozCraig Feb 08 '13 at 10:18
  • You are passing a const char *, not a string, try Output::Print(std::string("Hello World")) – jmetcalfe Feb 08 '13 at 10:19
  • 3
    Regarding "static": In C++, functions can be free from the tyranny of the class system, so you don't need to make quasi-classes like you must in Java. – molbdnilo Feb 08 '13 at 10:50
  • @molbdnilo, yeah I'm aware you can have classless functions, a bit like JavaScript I guess, where functions do not need to be tied to a prototype...again, as I am new to C++, I'm not sure what is the best practice here. – Matthew Layton Feb 08 '13 at 11:55

4 Answers4

63

"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"));
Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • 1
    @LuchianGrigore I concur that is the deeper question. I'm just not sure the OP even knows he asked it =P – WhozCraig Feb 08 '13 at 10:20
  • 1
    Yeah I tried that, which worked...just seems untidy!, I've changed the method declaration to Print(const char* value) which seems to work also...Not sure what is best practice here though! – Matthew Layton Feb 08 '13 at 10:22
  • @LuchianGrigore I've thrown that in there. – Joseph Mansfield Feb 08 '13 at 10:27
  • Yeah, saw that, much better if you ask me. +1 – Luchian Grigore Feb 08 '13 at 10:31
  • 1
    @series0ne You can actually have both overloads - on `std::string` (or preferably `const std::string&`) and on `const char*`. – Angew is no longer proud of SO Feb 08 '13 at 10:45
  • 3
    How to force an error or a "correct" (in the meaning of expected) translation if someone calls `Output::Print("Hello World")` which is translated into `Output::Print(bool value)` which is not what anyone expect? – kirsche40 Feb 17 '14 at 14:03
  • Is this a case where what the Standard thinks is better, and what J. Random User thinks is better, are *completely* at odds with each other? (Sorry; rhetorical question. It's just that I'm not convinced that J. Random User is wrong here.) – Steve Summit Aug 21 '18 at 19:07
  • @kirsche40 Templates and SFINAE. – Deduplicator Dec 05 '18 at 12:21
  • 1
    Do you know if there is a difference about the decision of performing the standard conversion between different versions of C++ ? i-e. Will C++17 and C++98 perform the standard conversion from const char * to bool in the exact same cases ? – Burgito Jan 24 '20 at 08:13
14

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));
    }
};
Zachary Canann
  • 1,131
  • 2
  • 13
  • 23
10

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);
}
akim
  • 8,255
  • 3
  • 44
  • 60
3

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

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Zamfir Yonchev
  • 151
  • 1
  • 10