Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

msvc /permissive- std::string overloaded operator '=' is ambiguous

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)'
like image 605
OwnageIsMagic Avatar asked Sep 06 '19 11:09

OwnageIsMagic


2 Answers

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).

like image 100
songyuanyao Avatar answered Nov 12 '22 02:11

songyuanyao


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
like image 22
Vlad from Moscow Avatar answered Nov 12 '22 02:11

Vlad from Moscow