It compiles with /permissive
but fails with /permissive-
. What is not conforming and how to fix it?
Why it's fine in (2)
but fails in (4)
(3)
?
If I remove operator long
it also fine.
How to fix it without changing call site (3,4)
?
#include <string>
struct my
{
std::string myVal;
my(std::string val): myVal(val) {}
operator std::string() { return myVal; };
operator long() { return std::stol(myVal); };
};
int main()
{
struct MyStruct
{
long n = my("1223"); // (1)
std::string s = my("ascas"); // (2)
} str;
str.s = my("ascas"); // (3)
str.n = my("1223"); // (4)
}
error message
error C2593: 'operator =' is ambiguous
xstring(2667): note: could be 'std::basic_string<...> &std::basic_string<...>::operator =(const _Elem)'
with
[
_Elem=char
]
xstring(2648): note: or 'std::basic_string<...> &std::basic_string<...>::operator =(const std::basic_string<...> &)'
xstring(2453): note: or 'std::basic_string<...> &std::basic_string<...>::operator =(std::basic_string<...> &&) noexcept(<expr>)'
Source1.cpp(17): note: while trying to match the argument list '(std::string, my)'
I suppose you meant it's fine in (2) but fails in (3)
Note that the #2 is initialization, which calls the constructor of std::string
; the #3 is assignment, which calls the assignment operator of std::string
. They're different things.
The invocation of assigment operator is ambiguous because the assignment operator of std::string
has an overload taking char
, which could be implicitly converted from long
(which is a stardard conversion), then leads to ambiguity (with the assignment operators taking std::string
, as the compiler complained). Both the implicit conversion sequence contain one user-defined conversion (from my
to std::string
or long
), they have the same rank in onverload resolution.
The invocation of constructor is fine because it doesn't have such overload (taking char
).
The problem is that in the case #2 there is used a constructor while in the case #3 there is used an assignment operator.
The assignment operator is overloaded like
basic_string& operator=(charT c);
But there is no constructor that accepts only one argument of the type charT
So for the case #2 there is used the user-defined conversion operator
operator std::string() { return myVal; };
and then the constructor
basic_string(basic_string&& str) noexcept;
In the case #3 there are two posiibilities.
The first one is to call the conversion operator
operator std::string() { return myVal; };
and then the assignment operator
basic_string& operator=(basic_string&& str)
And the second one is to call the conversion operator
operator long() { return std::stol(myVal); };
and then the assignment operator
basic_string& operator=(charT c);
It is interesting to note the following additional case.
If you will wrote
str.s = { my("ascas") };
then there will not be an ambiguity. The compiler will select the operator that accepts an std::initializer_list. That is it will select the assignment operator
basic_string& operator=(initializer_list<charT>);
In this case there will be used the conversion operator
operator long() { return std::stol(myVal); };
but as the string "ascas"
can not be converted to the type long a runtime error will occur
terminate called after throwing an instance of 'std::invalid_argument'
what(): stol
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