我试着编译这个测试代码
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对象的临时示例时,始终使用为右值定义的转换操作符。
1条答案
按热度按时间wnavrhmk1#
MyData data = foo();
查找构造函数和转换函数来执行请求的隐式转换。候选构造函数是不可行的,因为它们需要用户定义的转换来初始化第一个参数(这将导致使用两个用户定义转换的整体隐式转换)。
要选择两个转换函数中的一个,通常的重载解析规则适用。第二种情况下
Wrapper&&
隐式对象参数到foo()
的绑定优于Wrapper const&
隐式对象参数到foo() because
的绑定foo()'是右值。赋值的情况有点不同,它 * 必须 * 调用赋值运算符,候选的是
所以我们必须找到从
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 &&
),则认为没有一个比另一个更好。这使得重载解析不明确。