8

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.

12
  • 1
    With the copy-and-swap idiom, you don't need an additional move-assignment operator Commented Dec 6, 2018 at 13:39
  • copy-and-swap covers both const& and &&. Either do copy-and-swap (only one overload taking param by value) or implement const& and && overloads for operator= Commented Dec 6, 2018 at 13:40
  • Since you decided (why?) to do assignment by value, the argument can be passed through the move constructor. (By-value assignment only causes unnecessary copying. Don't do it.) Commented Dec 6, 2018 at 13:42
  • @molbdnilo that's an idiom, whether it's a copy or a move depends on the usage Commented Dec 6, 2018 at 13:44
  • 3
    @Dejan it's "four and a half" because an assignment operator accepting a parameter by value can be used for copy- and move-assignment. this is because the argument expression can be either an lvalue or rvalue, and the parameter will be either move- or copy-constructed from that argument expression. as such, you don't need to provide a copy-assignment operator nor a move-assignment operator, because the one accepting parameter by value allows you to construct that parameter itself though move or copy construction. once the parameter is constructed,its content is swapped with the implicit object Commented Dec 6, 2018 at 17:48

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.