#include <string>
struct String
{
template<typename T> operator T*() { return 0; }
operator std::string() { return ""; }
};
int main()
{
String myStr;
std::string str1(myStr); // ambiguous, error C2668
std::string str2 = myStr; // error C2440:
// 'initializing' : cannot convert from 'String' to
// `std::basic_string<char,std::char_traits<char>,std::allocator<char>>',
// No constructor could take the source type,
// or constructor overload resolution was ambiguous
const std::string& rStr = myStr; // Ok, but why?
}
I'm using VS 2013.
Questions:
Why do the definitions of str1
and str2
lead to different compile errors?
As I know, when rStr
is created, a temporary string object is created firstly, then rStr
will refer to the temporary. But, why does the creation of the temporary object not lead a compile error? Is there any different between tmp
and strN
?
The first definition, std::string str1(myStr);
is indeed ambigous:
std::string str1(myStr.operator char*());
// or
std::string str1(myStr.operator std::string());
so this initialization fails due to an ambiguity.
This is essentially the same scenario as
void foo(char const*);
void foo(std::string);
foo(myStr); // ambiguous
Exactly one user-defined conversion is required, then a function will be called (for the first definition, the function is a constructor). Both conversions are viable, and neither is a subset of the other, so both have the same rank.
The second definition, std::string str2 = myStr;
is actually fine. Only one user-defined conversion to std::string
is allowed, either via a constructor or via a conversion function, not both. So only std::string str2 = myStr.operator std::string();
is viable.
Note string str2 = expr;
when expr
is not of type string
requires the expr
to be converted to std::string
. The resulting temporary is then used to initialize str2
via a copy/move:
string str2 = string(expr);
// ~~~~~~ implicit
Therefore, the conversion on the right hand side must convert directly to std::string
, otherwise you would need a chain of two user-defined conversions to initialize the temporary: (UDC = User-Defined Conversion)
string str2 = string(expr);
// resolved as:
string str2 = expr.operator string(); // fine: one implicit UDC
string str2 = string(expr.operator char*()); // error: two UDCs
For example, expr
to char const*
via the operator char*
and then to a std::string
via the converting constructor requires a chain of two user-defined conversions => not viable. If we try to use the operator char*()
conversion, we need an additional constructor implicit constructor call to make the RHS a string
.
This is different from string str1( expr )
, where expr
does not need to be converted implicitly to string
. It might have to be converted to initialize a parameter of a string constructor. The direct initialization of str1
from the possibly converted expr
is not a(n implicit) conversion itself, but just a function call. No extra temporary is created:
string str1( expr );
// resolved as:
string str1( expr.operator string() ); // fine
string str1( expr.operator char* () ); // fine
This second definition is refused when compiling with enabled language extension. Without language extensions, this initialization is fine in VS2013 Update 2.
The third one follows a different initialization scheme. It should behave like the second one in this case, as far as I can tell. The language extensions seems to apply only to the second one but not to the third one, it seems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With