c++ 左值和右值情况下具有不同用户强制转换运算符的重载不明确

mpgws1up  于 2023-03-05  发布在  其他
关注(0)|答案(1)|浏览(99)

我试着编译这个测试代码

struct MyData { /*..*/ };

template < typename T >
struct Wrapper
{
    T m_value;

    operator T const & () const & { return m_value; }
    operator T && () && { return ::std::move(m_value); }
};

using MyWrapper = Wrapper< MyData >;

MyWrapper foo () { return {}; }

int main ()
{
    MyData data = foo();    // ok
    data = foo();           // error: ambiguous overload for 'operator='

    return 0;
}

我定义了两个类型转换操作符,一个用于左值,一个用于右值。我希望在使用 Package 对象的临时示例时,总是使用为右值定义的转换操作符。在对象构造的情况下为真,而在对象赋值的情况下为假。为什么?
这段代码用gcc7.5编译并按预期工作。编译器gcc8和更高版本,clang和msvc不编译这段代码。
我尝试编译代码,并希望在使用Wrapper对象的临时示例时,始终使用为右值定义的转换操作符。

wnavrhmk

wnavrhmk1#

MyData data = foo();查找构造函数和转换函数来执行请求的隐式转换。

MyData(MyData const&);  // implicitly declared
MyData(MyData&&);       // implicitly declared
operator MyData const & () const &;
operator MyData && () &&;

候选构造函数是不可行的,因为它们需要用户定义的转换来初始化第一个参数(这将导致使用两个用户定义转换的整体隐式转换)。
要选择两个转换函数中的一个,通常的重载解析规则适用。第二种情况下Wrapper&&隐式对象参数到foo()的绑定优于Wrapper const&隐式对象参数到foo() because的绑定foo()'是右值。
赋值的情况有点不同,它 * 必须 * 调用赋值运算符,候选的是

operator=(MyData const&);  // implicitly declared
operator=(MyData&&);       // implicitly declared

所以我们必须找到从foo()const MyData&的隐式转换序列,以及从foo()MyData&&的隐式转换序列,并决定哪个隐式转换序列更好。这将决定所选择的重载。这些隐式转换序列是:

  • 绑定operator MyData const &的隐式对象参数(类型为Wrapper const&),然后调用operator MyData const &,然后将MyData const&绑定到结果(标识转换)
  • 绑定operator MyData &&的隐式对象参数(类型为Wrapper &&),然后调用operator MyData &&,然后将MyData&&绑定到结果(标识转换)

隐式转换序列的排名规则规定,如果两个用户定义的转换序列使用不同的用户定义转换(这里就是这种情况,一个使用operator MyData const &,而另一个使用operator MyData &&),则认为没有一个比另一个更好。这使得重载解析不明确。

相关问题