Why is it inefficient to use a single assignment operator that performs both copy and move?

Here is an exercise from C ++ Primer 5th Edition:

Exercise 13.53: As a HasPtr low-level efficiency, the assignment operator is not ideal. Explain why. Implement copy-assign and assignment operator for HasPtr and compare the operations performed in your new redirection operator compared to the copy and swap version (P.544)

hasptr.h file:

 //! a class holding a std::string* class HasPtr { friend void swap(HasPtr&, HasPtr&); friend bool operator <(const HasPtr& lhs, const HasPtr& rhs); public: //! default constructor. HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { } //! copy constructor. HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { } //! move constructor. HasPtr(HasPtr&& hp) noexcept : ps(hp.ps), i(hp.i) { hp.ps = nullptr; } //! assignment operator HasPtr& operator = (HasPtr rhs); //! destructor. ~HasPtr() { delete ps; } private: std::string *ps; int i; }; 

Part of the hasptr.cpp file:

 //! specific swap. inline void swap(HasPtr &lhs, HasPtr &rhs) { using std::swap; swap(lhs.ps, rhs.ps); // swap the pointers, not the string data swap(lhs.i, rhs.i); // swap the int members std::cout <<"swapping!\n"; } //! operator = using specific swap HasPtr& HasPtr::operator = (HasPtr rhs) { swap(*this,rhs); return *this; } 

My question is: why is this not effective?

+6
source share
2 answers

Step 1

Set up a performance test that the move assignment operator performs.

Configure another performance test that the copy assignment statement performs.

Step 2

Configure the assignment operator in both directions as indicated in the problem instruction.

Step 3

Iterate through steps 1 and 2 until you are sure you did them right.

Step 3 should help tell you what is going on, most likely telling you where performance is changing and where it is not changing.

Guessing is not an option for steps 1-3. You really have to fulfill them. Otherwise, you (correctly) are not sure that your guesses are correct.

Step 4

Now you can start to guess. Some people will call this "hypothesis formation." A fancy way of saying guess. But at least now it is guessed.

I went through this exercise to answer this question, and did not notice a significant difference in performance for one test and the difference in performance 6X on the other. This led me to a hypothesis. After completing this work, if you are unsure of your hypothesis, update your question using your code, results, and subsequent questions.

Explanation

There are two special member assignment operators that usually have signatures:

 HasPtr& operator=(const HasPtr& rhs); // copy assignment operator HasPtr& operator=(HasPtr&& rhs); // move assignment operator 

You can implement both redirection assignment and copy assignment using a single assignment operator with the so-called copy / swap idiom:

 HasPtr& operator=(HasPtr rhs); 

This single assignment statement cannot be overloaded with the first set.

Is it better to implement two assignment operators (copy and move) or only one using the copy / swap icon? This is what Exercise 13.53 sets. To answer, you should try both methods and measure both the purpose of the copy and move the purpose. And smart, well-meaning people make mistakes, guessing, instead of testing / measuring. You have chosen a good exercise to learn.

+10
source

As the problem shows, this is a "low level efficiency issue." When you use HasPtr& operator=(HasPtr rhs) and you write something like hp = std::move(hp2); , the ps member is copied twice (the pointer itself is not the object it points to): once from hp2 to rhs as a result of calling the move constructor and once from rhs to *this as a result of calling swap . But when you use HasPtr& operator=(HasPtr&& rhs) , ps is only copied once from rhs to *this .

+1
source

All Articles