简体   繁体   中英

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

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.

Using forwarding reference && will bind to any rvalue's ( temporaries / forwarding references ). These categories can also be passed as value and is then ambiguous. That is, a temporary for example is ambiguous where as an lvalue will not be (uses value overload).

Where as the non-const reference can never bind to a temporary or forwarding reference . A const reference can though, but isn't ambiguous.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM