While using the copy-and-swap idiom in a class that has constant references as members, the above error occurs.
Example code:
#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')
}
When the Copy assignment operator is being changed to take its argument by-reference, instead of by-value, the error is fixed.
inline I_hold_reference& operator=(I_hold_reference& other){ ... }
Why does this fix the error? One possible implication would be that the Important optimization possibility referenced in the linked question is lost. Was that ever true for references? What are the other implications of this change?
There is a codebase relying on this operator, no other members are present, only the mentioned references. Is there a need to adapt the codebase to this change somehow, or is it safe as-is?
If you carefully follow the description you linked, you will see that you must have only one overload of operator=
and that one needs to take its argument by value. So, simply removing the operator=(I_hold_reference&&)
overload will make your code compilable.
However, this is not the only issue. Your swap
doesn't swap! Instead, it assigns a copy of second
to first
and leaves second
untouched.
This is what you want:
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;
};
Note: I removed unnecessary inline
s as member functions are implicitly inline. Also I declared the swap
function inside of your class. You can find an explanation for this in the link you shared.
Also, in this particular example, using the copy-and-swap idiom is unnecessary in the first place. std::reference_wrapper
is not a manually maintained resource, meaning it has proper copy and move semantics built in. So, in this particular example, the compiler generated copy and move operators will have the exact same behavior as the manually created one here. So, you should use those and not write you own ones in whatever way. On the other hand, if this is just a toy example and there are more resource in the real class that do need manual management, then this is the way to go.