Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the wrong ctor being called?

I have code that works as expected:

EscapedString es("Abc&def");
EscapedString es2("");
es2 = es;     // es2 == Abc%26def

And code that doesn't work as expected:

EscapedString es("Abc&def");
EscapedString es2 = es;    // es == Abc%2526def

In the second case, the CTOR2 is called instead of CTOR3 even though es is an EscapedString.

EscapedString es(EscapedString("Abc?def"));

Does the right thing, but I can't seem to set a breakpoint on CTOR3 so I'm not sure it is working correctly, or the code has been optimized away or it is accidentally working.

The class is below:

class EscapedString : public std::string {
public:
    EscapedString(const char *szUnEscaped) {  // CTOR1
        *this = szUnEscaped;
    }

    EscapedString(const std::string &strUnEscaped) {   // CTOR2
        *this = strUnEscaped;
    }

    explicit EscapedString(const EscapedString &strEscaped) {   // CTOR3
        *this = strEscaped;  // Can't set breakpoint here
    }

    EscapedString &operator=(const std::string &strUnEscaped) {
        char *szEscaped = curl_easy_escape(NULL, strUnEscaped.c_str(), strUnEscaped.length());
        this->assign(szEscaped);
        curl_free(szEscaped);
        return *this;
    }
    EscapedString &operator=(const char *szUnEscaped) {
        char *szEscaped = curl_easy_escape(NULL, szUnEscaped, strlen(szUnEscaped));
        this->assign(szEscaped);
        curl_free(szEscaped);
        return *this;
    }
    EscapedString &operator=(const EscapedString &strEscaped) {
        // Don't re-escape the escaped value
        this->assign(static_cast<const std::string &>(strEscaped));
        return *this;
    }
};
like image 911
boatcoder Avatar asked Dec 26 '22 02:12

boatcoder


1 Answers

Normally, EscapedString es2 = es; will call the copy constructor, however you explicitly told it not to by making the copy constructor explicit:

explicit EscapedString(const EscapedString &strEscaped)

A constructor marked explicit can never be called by means of automatic type conversions. It can only be called, well... explicitly, which you have done here:

EscapedString es(EscapedString("Abc?def"));

Here's what happens when the compiler encounters EscapedString es2 = es;.

First the compiler sees if it can use the copy constructor and finds that it can't, because it was marked explicit. So it looks for another constructor to call. Since EscapedString is derived from std::string, the compiler is able to cast es as a const std::string& and call:

EscapedString &operator=(const std::string &strUnEscaped)
like image 64
John Dibling Avatar answered Jan 05 '23 22:01

John Dibling