Different behavior between explicit cast, direct initialization, and copy initialization

I have a class C that has a cast operator. In the example, I tried to cast an instance of it to std::string three different ways: static_cast , constructor std::string and assigning std::string . However, compiling only the latest compilation, while others create an ambiguous constructor error.

The cause of the error is clear enough: there are many ways to convert C to what the std::string constructor can take. But what is the difference between these cases? Why does operator casting work as intended, 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 mentioned in the comments of the balls, this question does not 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 described errors.

+7
c ++ casting initialization language-lawyer templates
source share
1 answer

Before C ++ 17

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

T constructors are considered, and the best match is selected by overload resolution. Then the constructor is called to initialize the object.

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

string ok = c copies the initialization (note that this is not an assignment), then

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

This means that checking from C to std::string checked and used for initialization here.

After C ++ 17

Since C ++ 17 is for direct initlizatioin ,

if the initializer is a prvalue expression whose cv-unqualified type is the same class as T , the initialization expression itself, but rather a temporary materialized one, is used to initialize the target: see copy elision (starting with C ++ 17)

This means that the conversion from C to std::string overwritten and used for initialization, then the ambiguity disappears and the code works well.

Live

+6
source share

All Articles