Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different behavior among explicit cast, direct initialization and copy initialization

I have a class C which has a casting operator to anything. In the example I tried to cast an instance of it to std::string in three different ways: static_cast, constructor of std::string and assigning to std::string. However, only the last one compiles, while the others raise an error of ambiguous constructor.

The reason of the error is clear enough: there are many ways to convert C to something which the constructor of std::string can accept. But what is the difference between these cases? Why cast operator works as intended here but not there?

struct C {
    template<typename T>
    operator T() const {
        return T{};
    }
};

int main() {
    C c;

    cout << static_cast<string>(c) << endl; // compile error
    string bad(c); // compile error
    string ok = c; // compiles successfully
}

UPD: as bolov mentioned in comments, this issue doesn't reproduce with C++17. I tested it with g++-5 and clang-3.8 with -std=c++11 and -std=c++14, and it shows the described errors.

like image 699
Ivan Smirnov Avatar asked Oct 14 '17 02:10

Ivan Smirnov


People also ask

What is the difference between direct initialization and copy initialization?

The main difference between these two types of initialization is that the copy initialization creates a separate memory block for the new object. But the direct initialization does not make new memory space. It uses reference variable to point to the previous memory block.

What is the difference between initialization and assignment in C++?

What is the difference between initialization and assignment? Initialization gives a variable an initial value at the point when it is created. Assignment gives a variable a value at some point after the variable is created.

What is direct initialization in CPP?

Direct initialization applies when initializing objects without explicit assignment. It also is also used for explicit casts, whether function-style or via static_cast and applies to Lambda closure arguments captured by value (which may be regarded as a special case of member initialization).

Does copy initialization call copy constructor?

In other words, a good compiler will not create a copy for copy-initialization when it can be avoided; instead it will just call the constructor directly -- ie, just like for direct-initialization.


1 Answers

Before C++17

static_cast<string>(c) and string bad(c) performs direct initialization, then

the constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.

As you said, all the possible constructors of std::string are examined and C can be converted to anything required, then causes ambiguity.

string ok = c performs copy initialization (note it's not assignment), then

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution.

That means the conversion from C to std::string is examined, and used for the initialization here.

After C++17

Since C++17 for direct initlizatioin,

if the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision (since C++17)

That means the conversion from C to std::string is perferred and used for the initialization, then the ambiguity disappears and the code works well.

LIVE

like image 102
songyuanyao Avatar answered Oct 27 '22 08:10

songyuanyao