开发者

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:开发者_StackOverflow中文版

    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) ?


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.


"" 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.


Because a user-defined conversion is not considered if a built-in one is available.

Use the third constructor, one that takes const char*. There's no better way.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜