Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++: operator = is ambiguous when implementing move assignment

I am trying to implement a rule of five for the first time. After reading a lot of recommendation about best practices, I end up with a solution where copy/move assignment operators seem to be in some conflict.

Here is my code.

#include <vector>   
#include <memory>

template<class T> class DirectedGraph {
public:
    std::vector<T> nodes;
    DirectedGraph() {}
    DirectedGraph(std::size_t n) : nodes(n, T()) {}
    // ... Additional methods ....
};

template<class T>
DirectedGraph<T> Clone(DirectedGraph<T> graph) {
    auto clone = DirectedGraph<T>();
    clone.nodes = graph.nodes;
    return clone;
}

template<class T> class UndirectedGraph
{
    using TDirectedG = DirectedGraph<T>;
    using TUndirectedG = UndirectedGraph<T>;

    std::size_t numberOfEdges;
    std::unique_ptr<TDirectedG> directedGraph;
public:
    UndirectedGraph(std::size_t n)
        : directedGraph(std::make_unique<TDirectedG>(n))
        , numberOfEdges(0) {}

    UndirectedGraph(TUndirectedG&& other) {
        this->numberOfEdges = other.numberOfEdges;
        this->directedGraph = std::move(other.directedGraph);
    }

    UndirectedGraph(const TUndirectedG& other) {
        this->numberOfEdges = other.numberOfEdges;
        this->directedGraph = std::make_unique<TDirectedG>
            (Clone<T>(*other.directedGraph));
    }

    friend void swap(TUndirectedG& first, TUndirectedG& second) {
        using std::swap;
        swap(first.numberOfEdges, second.numberOfEdges);
        swap(first.directedGraph, second.directedGraph);
    }

    TUndirectedG& operator=(TUndirectedG other) {
        swap(*this, other);
        return *this;
    }

    TUndirectedG& operator=(TUndirectedG&& other) {
        swap(*this, other);
        return *this;
    }

    ~UndirectedGraph() {}
};

int main()
{
    UndirectedGraph<int> graph(10);
    auto copyGraph = UndirectedGraph<int>(graph);
    auto newGraph = UndirectedGraph<int>(3);
    newGraph = graph;            // This works.
    newGraph = std::move(graph); // Error here!!!
    return 0;
}

Most of the recommendations I took from here, and I implemented copy assignment operator= to accept parameter by value. I think this might be a problem, but I don't understand why.

Additionally, I would highly appreciate if someone point out whether or not my copy/move ctor/assignments are implemented in the correct way.

like image 296
Dejan Avatar asked Dec 06 '18 13:12

Dejan


People also ask

Which is true about move operations move constructor move assignment operator?

The move assignment operator is different than a move constructor because a move assignment operator is called on an existing object, while a move constructor is called on an object created by the operation. Thereafter, the other object's data is no longer valid.

What does a move assignment operator do?

Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state.

Does move assignment call destructor?

c++ - Default move assignment calls destructor, copy assignment doesn't - Stack Overflow. Stack Overflow for Teams – Start collaborating and sharing organizational knowledge.

How do you write a move assignment operator?

To create a move assignment operator for a C++ class In the move assignment operator, add a conditional statement that performs no operation if you try to assign the object to itself. In the conditional statement, free any resources (such as memory) from the object that is being assigned to.


1 Answers

You should have:

TUndirectedG& operator=(const TUndirectedG&);
TUndirectedG& operator=(TUndirectedG&&);

or

TUndirectedG& operator=(TUndirectedG);

Having both

TUndirectedG& operator=(TUndirectedG);   // Lead to ambiguous call
TUndirectedG& operator=(TUndirectedG&&); // Lead to ambiguous call

would lead to ambiguous call with rvalue.

like image 115
Jarod42 Avatar answered Sep 28 '22 12:09

Jarod42