问题
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.
回答1:
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 ofother
is notT
or derived fromT
, or ifT
is non-class type, but the type ofother
is a class type, user-defined conversion sequences that can convert from the type ofother
toT
(or to a type derived fromT
ifT
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
来源:https://stackoverflow.com/questions/46740341/different-behavior-among-explicit-cast-direct-initialization-and-copy-initializ