错误:使用复制和交换习惯用法的交换函数中"operator="的重载不明确

error: ambiguous overload for 'operator=' in swap function using the copy-and-swap idiom

本文关键字:交换 函数 operator 不明确 重载 惯用法 复制 习惯 错误      更新时间:2023-10-16

在具有常量引用作为成员的类中使用复制和交换习惯用法时,出现上述错误。

示例代码:

#include <iostream>
#include <functional>
using std::reference_wrapper;
class I_hold_reference;
void swap(I_hold_reference& first, I_hold_reference& second);
class I_hold_reference{
inline I_hold_reference(const int& number_reference) : my_reference(number_reference){}
friend void swap(I_hold_reference& first, I_hold_reference& second);
inline I_hold_reference& operator=(I_hold_reference other){
swap(*this, other);
return *this;
}
inline I_hold_reference& operator=(I_hold_reference&& other){
swap(*this, other);
return *this;
}
private:
reference_wrapper<const int> my_reference;
};
void swap(I_hold_reference& first, I_hold_reference& second){
first = I_hold_reference(second.my_reference); //error: use of overloaded operator '=' is ambiguous (with operand types 'I_hold_reference' and 'I_hold_reference')
}

当Copy赋值运算符被更改为通过引用而不是通过值来获取其参数时,错误就会得到修复。

inline I_hold_reference& operator=(I_hold_reference& other){ ... }

为什么这样可以修复错误?一个可能的含义是,链接问题中引用的重要优化可能性丢失。参考资料是这样的吗?这一变化的其他含义是什么?

有一个依赖于这个运算符的代码库,没有其他成员,只有提到的引用。是否需要以某种方式调整代码库以适应这种变化,或者它是安全的?

如果您仔细遵循链接的描述,您将看到您必须只有一个operator=重载,并且该重载需要按值获取其参数。因此,简单地删除operator=(I_hold_reference&&)重载就会使代码变得可编译。

然而,这并不是唯一的问题。你的swap不会交换!相反,它将second的副本分配给first,并保持second不变。

这就是你想要的:

class I_hold_reference
{
I_hold_reference(const int& number_reference)
: my_reference(number_reference){}
friend void swap(I_hold_reference& first, I_hold_reference& second)
{
using std::swap;
swap(first.my_reference, second.my_reference);
}
I_hold_reference& operator=(I_hold_reference other)
{
swap(*this, other);
return *this;
}
private:
reference_wrapper<const int> my_reference;
};

注意:我删除了不必要的inline,因为成员函数是隐式内联的。此外,我还在类中声明了swap函数。你可以在你分享的链接中找到对此的解释。

此外,在这个特定的示例中,首先不需要使用复制和交换习惯用法。std::reference_wrapper不是一个手动维护的资源,这意味着它内置了正确的复制和移动语义。因此,在这个特定的示例中,编译器生成的复制和移运算符将具有与此处手动创建的运算符完全相同的行为。所以,你应该使用这些,而不是以任何方式写你自己的。另一方面,如果这只是一个玩具示例,并且实际类中有更多的资源需要手动管理,那么这就是我们要走的路。

使用转发引用&&将绑定到任何右值(临时/转发引用(。这些类别也可以作为值传递,然后是不明确的。也就是说,例如,临时是不明确的,因为左值不会是(使用值重载(。

其中,作为非常量引用,永远不能绑定到临时转发引用常量引用可以,但并不含糊。