msvc /permissive- std::string 重载运算符“=”不明确

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

它用 /permissive 编译,但用 /permissive- 编译失败。什么不符合要求以及如何解决?

为什么在 (2) 中没问题,但在 (4)(3) 中失败了? 如果我删除 operator long 也可以。

如何在不更改调用站点的情况下修复它(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 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)'

我想你的意思是 它在 (2) 中很好,但在 (3) 中失败

注意 #2 是初始化,调用 constructor of std::string; the #3 is assignment, which calls the assignment operator of std::string。它们是不同的东西。

赋值运算符的调用不明确,因为 std::string 的赋值运算符有一个重载 char,它可以从 long 隐式转换(这是标准转换) ,然后导致歧义(编译器抱怨赋值运算符采用 std::string)。 implicit conversion sequence contain one user-defined conversion (from my to std::string or long), they have the same rank in onverload resolution.

构造函数的调用没问题,因为它没有这样的重载(取char)。

问题是在案例 #2 中使用了构造函数,而在案例 #3 中使用了赋值运算符。

赋值运算符重载如

basic_string& operator=(charT c);

但是没有构造函数只接受一个charT

类型的参数

因此对于案例 #2,使用了用户定义的转换运算符

operator std::string() { return myVal; };

然后是构造函数

basic_string(basic_string&& str) noexcept;

在案例 #3 中,有两种可能性。

第一个是调用转换运算符

operator std::string() { return myVal; };

然后是赋值运算符

basic_string& operator=(basic_string&& str)

而第二个是调用转换运算符

operator long() { return std::stol(myVal); };

然后是赋值运算符

basic_string& operator=(charT c);

值得注意的是以下附加案例。

如果你会写

str.s = { my("ascas") };

那就不会有歧义了。编译器将 select 接受 std::initializer_list 的运算符。即它将 select 赋值运算符

basic_string& operator=(initializer_list<charT>);

在这种情况下,将使用转换运算符

operator long() { return std::stol(myVal); };

但由于字符串"ascas"无法转换为long类型,会出现运行时错误

terminate called after throwing an instance of 'std::invalid_argument'
  what():  stol

来自莫斯科的弗拉德回答有很好的解释。但是没有解决办法。 这里使用的是 SFINAE

template<typename T = long, typename = std::enable_if_t<
    std::is_same_v<T, long> || std::is_same_v<T, int>>>
operator T() const
{
    return l();
}

operator std::string() const
{
    return s();
}