9

I expect the code below to print Test::Test(string,string,bool), however it prints Test::Test(string,bool). Why does it call the constructor that takes only one string parameter when two are provided? Surely a string can't be converted to a bool...? I have tried adding the explicit keyword but it does not help. Code is also at http://ideone.com/n3tep1.

#include <iostream>
#include <string>

using namespace std;

class Test
{
public:

    Test(const string& str1, bool flag=false)
    {
        cout << "Test::Test(string,bool)" << endl;
    }

    Test(const string& str1, const string& str2, bool flag=false)
    {
        cout << "Test::Test(string,string,bool)" << endl;
    }
};

int main()
{
    Test* test = new Test("foo", "bar");
}
Evan Carslake
  • 2,267
  • 15
  • 38
  • 56
user1775138
  • 230
  • 1
  • 4
  • 7
    `"bar"` is not an `std::string`, it is a `const char*`, and a pointer can be implicitly converted to a `bool`, and that conversion must have precedence over the `std::string( const char* )` constructor. – clcto Aug 27 '15 at 15:40
  • The first version is a better match, try new Test("foo", std::string("bar")) – Melkon Aug 27 '15 at 15:41
  • 6
    On a somewhat related note, you generally don't want to use `new` very often in C++ code. Stack variables are preferred when possible. Just a heads-up since you say you've been away for a while. – dandan78 Aug 27 '15 at 15:44
  • Thanks. I'm using C++11 and some other things I bumped into recently led me to believe that string literals were treated as std::string, but I guess they were just being implicitly converted and that there were no competing conversions such as the ptr -> bool one that I'm hitting here. I was avoiding the new, but added it while messing with the code. – user1775138 Aug 27 '15 at 15:52
  • I had the same issue and solved it by changing the `bool` to `int`/`char`, because I really dislike having to remember to write an explicit string construction in the call like @Melkon suggested. – Felix Dombek Aug 27 '15 at 16:17
  • There was a [question](http://stackoverflow.com/questions/32116430/default-advice-for-using-c-style-string-literals-vs-constructing-unnamed-stds) recently about when using user defined string literals would be useful. This would be a good example. – sjdowling Aug 27 '15 at 16:28

3 Answers3

7

The type of the argument being used to construct Test is char const[4].

char const[4] decays to char const*, and has to be converted to a bool or a std::string const& to make the function call unambiguous.

A pointer can be converted to bool using standard conversion rules.

A char const* can be converted to std::string const& using a user defined conversion rule.

Given that, the conversion from char const*, a pointer, to bool is considered a better match than the conversion from char const* to std::string const&.

Hence, the call resolves to the first constructor.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

"bar" is of type char const [4], and the conversion from that to a bool is an standard conversion sequence, while conversion to std::string is a user defined conversion. The former is always preferred over the latter.

From N3337, [conv]/1

Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:
    — Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
    — Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
    — Zero or one qualification conversion.

In your example, the standard conversion sequence consists of array-to-pointer conversion and boolean conversion.

[conv.array]/1

An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

[conv.bool]/1

A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. ...

Thus Test("foo", "bar") results in a call to the Test(const string&, bool) constructor instead of the other one.


One way to trigger a call to the other constructor would be to use string_literals

using namespace std::literals::string_literals;
Test("foo", "bar"s);  // calls Test(const string&, const string&, bool)
//               ^ 
Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

I would expect that if anything the compiler would complain about ambiguous functions.

The reason I would expect that, and that you get the wrong constructor being called, is that using a string literal results in a pointer, and pointer can implicitly be converted to a boolean value.

In this case the compiler apparently thinks that the boolean conversion is a better match than conversion to a std::string.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621