Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with constructor resolution order

Consider the following constructors for T:

struct T {
    T(const bool) { std::cout << "T(const bool)" << endl; }
    T(const std::string&) { std::cout << "T(const std::string&)" << endl; }
};

T t("");
  1. Why does T(const bool) take precedence over T(const std::string&) when constructing t?
  2. Since the above precedence might cause confusion to users expecting T(const std::string&) to be called, what can I do to have T(const std::string&) implicitly called when passing string literals to the constructor of T. For now the only work around I have found is to add another constructor, which takes the highest precedence:

    T(const char* s)
    {
        std::cout << "T(const char*)" << endl;
        *this = std::string(s);
    }
    
  3. Apart from the above solution, declaring explicit T(const bool) to avoid confusion does not solve the above problem: in this case, although T t = "" is now prohibited, why is the form T t("") is still allowed and does call T(const bool) ?

like image 685
pipex Avatar asked Aug 18 '11 05:08

pipex


2 Answers

Why does T(const bool) take precedence over T(const std::string&) when constructing t?

"" is of type char[1]; this is implicitly convertible to char const* via the array-to-pointer conversion. A pointer is implicitly convertible to bool, with all non-null pointers becoming true and all null pointers becoming false. These are both "built-in" standard conversions.

The char const* -> std::string conversion is a user-declared conversion: it utilizes a converting constructor of std::string that takes a char const*.

The standard ("built-in") conversions are preferred over user-declared conversions during overload resolution, so the constructor taking bool is a better match here than the one taking std::string.

For now the only work around I have found is to add another constructor

That sounds like a reasonable solution; certainly the most straightforward solution for the simple scenario you describe. Your use of assignment to *this is a bit clumsy, though; it would be better either to have both constructors delegate to some initialization function.

Alternatively, you could use a template with enable_if for any constructors for which you would like to disallow conversions:

template <typename U>
T(U, std::enable_if<std::is_same<U, bool>::value>::type* = 0) { }

This constructor will only be callable with a bool argument and nothing else. You can find enable_if and is_same in Boost, C++ TR1, or C++0x. You could also use !is_pointer, is_integral, or some other combination of type traits to allow for some other argument types but not char const*.

Or, as another alternative, you might eschew bool altogether and use your own enumeration with enumerators corresponding to true and false for the constructor. Whether this makes sense depends on your use case.

Declaring explicit T(const bool) to avoid does not solve the above problem...why is the form T t("") is still allowed and does call T(const bool)?

explicit only disallows implicit conversions to T. T t(""); has no conversions to T at all; it directly initializes the object t by constructing it with the argument "" passed to whichever constructor matches best.

like image 176
James McNellis Avatar answered Oct 26 '22 23:10

James McNellis


"" can convert to both std::string and bool.

The question is, which way will it convert?

  • Conversion to std::string is a user-defined conversion.
  • Conversion to bool is a standard conversion.

So the answer is, standard conversion has higher precedence over user-defined conversion. So "" will convert to bool.

Example,

struct A
{
   A(int i) {} //i.e an int can implicity convert to A
};

void f(const A &) { cout << "User-defined conversion won" << endl; }
void f(const bool &) { cout << "Standard conversion won" << endl; }

int main() {
        f (10);
        return 0;
}

Output:

Standard conversion won

Online demo : http://www.ideone.com/5Bt0K

In the above demo, 10 can convert to A and bool both. Since converting to bool is a standard conversion, it converts into bool, instead of A.

like image 27
Nawaz Avatar answered Oct 26 '22 23:10

Nawaz