Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ wrong constructor being called [duplicate]

Tags:

c++

c++11

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");
}
like image 419
user1775138 Avatar asked Aug 27 '15 15:08

user1775138


2 Answers

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.

like image 97
R Sahu Avatar answered Oct 27 '22 07:10

R Sahu


"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)
//               ^ 
like image 26
Praetorian Avatar answered Oct 27 '22 08:10

Praetorian