13

Why is C++ casting the string literal I pass in as a bool rather than a string?

#include <iostream>

using namespace std;

class A
{
    public:
        A(string v)
        {
            cout << v;
        }

        A(bool v)
        {
            cout << v;
        }
};

int main()
{
    A("hello");
    return 0;
}

Output: 1

Is it because the compiler isn't smart enough to make the jump from char * to string and rather just assumes that bool is the closest thing to a pointer? Is my only option to make an explicit char * constructor that basically does the exact same thing as the string constructor?

Gillespie
  • 5,780
  • 3
  • 32
  • 54
  • 1
    I generally prefer explicit conversions. Implicit conversions have gotchas like this. See http://stackoverflow.com/q/2346083/10077 – Fred Larson Oct 16 '14 at 21:18
  • I'm not sure but: Both conversions are possible (converting pointer to bool checks for null pointer). But converting to bool is a *built-in* conversion, while to string is a *user-defined* convesion (via implicit c'tor). And now if I remember correctly, built-ins have more priority over user defined ones. – leemes Oct 16 '14 at 21:19
  • 1
    possible duplicate of [C++ Method overload not working](http://stackoverflow.com/questions/14770252/c-method-overload-not-working) – M.M Oct 16 '14 at 21:57

4 Answers4

19

If you have C++11 you can use a delegating constructor:

A(char const* s) : A(std::string(s)) { }

The reason the boolean converting-constructor is chosen over the one for std::string is because the conversion from char const* to bool is a standard conversion while the one to std::string is a user-defined conversion. Standard conversions have a greater rank than user-defined conversions.

David G
  • 94,763
  • 41
  • 167
  • 253
5

With

A(string("hello"));

it will give the expected result.

Why is it so ?

It's because of the standard conversions:

  • "hello" is understood as a const char* pointer
  • this pointer can be converted to a bool (section 4.12 of the standard: "A prvalue of (...) pointer (...) type can be converted to a prvalue of type bool." )
  • the conversion from "hello" to string is not considered because section 12.3 of standard explains that "Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions" and "User-defined conversions are applied only where they are unambiguous". Wouldn't you have the bool constructor the std::string conversion would be done implicitely.

How to get what you expected ?

Just add the missing constructor for string litterals:

A(const char* v)
{
    cout << v;  // or convert v to string if you want to store it in a string member
}

Of course, instead of rewriting a constructor from scratch, you could opt for a delegate as suggested by 0x499602D2 in another answer.

Christophe
  • 68,716
  • 7
  • 72
  • 138
3

Recently I passed this problem too, let me share another way.

You can change the bool constructor to a unsigned char. So the decay and implict conversion of string literal don't happen, and the std::string constructor takes place.

class A
{
public:
    A(string v)
    {
        cout << v;
    }

    A(unsigned char v)
    {
        cout << static_cast<bool>(v);
    }
};

int main()
{
     A("Hello"); // <- Call A(string)
     A(false);   // <- Call A(unsigned char)
}

This way you don't need to provide always overloads to std::string and const char* neither making code bloat constructing the std::string at the client call site.

I don't claim that's better, but it's simpler.

Nico Engels
  • 407
  • 2
  • 12
  • I had the same problem, but I'm wondering though why A("Hello"); does not give a warning, but const char *b = "Hello"; A(b); gives warning 4800: forcing value to bool 'true' or 'false'? – shadow_map Feb 09 '17 at 11:10
  • This also worked for me and is also extremely useful with the new `string_view`s. See [here](https://godbolt.org/g/1uCwR2) for an example. – Claudius Feb 08 '18 at 09:51
2

When selecting an overloaded method this is the order that the compiler tries:

  1. Exact match
  2. Promotion
  3. Standard numerical conversion
  4. User defined operators

A pointer doesn't promote to bool but it does convert to bool. A char* uses an std operator to convert to std::string. Note that if char* used the same number in this list to convert to both bool and std::string it would be ambiguous which method the compiler should choose, so the compiler would throw an "ambiguous overload" error.

I would throw my weight behind 0x499602D2's solution. If you have C++11 your best bet is to call: A(char* s) : A(std::string(s)){} If you don't have C++11 then I would create an A(char* s) constructor and abstract the logic of the A(std::string) constructor into a method and call that method from both constructors.

http://www.learncpp.com/cpp-tutorial/76-function-overloading/

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288