📜 ⬆️ ⬇️

Move semantics in C ++ 11 and STL containers

This is a small note about how with the advent of the new standard C ++ 11 standard container requirements for their elements have changed. In C ++ 98, the container element was required, in fact, to have a “reasonable” copy constructor and an assignment operator. If, for example, an object in your class owns a resource, copying usually becomes impossible (at least without a "deep" copy of the resource). As an example, let's consider the following wrapper class around FILE* written in C ++ 98:

 class File { FILE* handle; public: File(const char* filename) { if ( !(handle = fopen(filename, "r")) ) throw std::runtime_error("blah blah blah"); } ~File() { if (handle) fclose(handle); } // ... private: File(const File&); //  void operator=(const File&); //  }; 



We have forbidden copying and assigning objects of this class, since copying FILE* would require some platform-dependent tweaks, and does not have a special physical meaning at all.
')
What to do if you want to keep a whole list of objects of type File ? Unfortunately, we cannot use File in a standard container, that is, such code simply does not compile:

 std::vector<File> files; files.push_back(File("data.txt")); 


A typical solution to this problem in C ++ 98 is to use shared_ptr :
 std::vector<boost::shared_ptr<File> > files; files.push_back(boost::shared_ptr<File>(new File("data.txt")) ); 


Such a solution is not particularly pleasing to the eye, especially considering that we use dynamic memory where it would seem that it is not needed.

If we allow the use of C ++ 11, then the picture changes dramatically. With the advent of move semantics , standard containers no longer require the usual copy constructor and assignment operator, unless you intend to copy the entire container. Instead, the semantics of displacement is sufficient. Let's see how we can rewrite the example of the File class in C ++ 11:

 class File { FILE* handle; public: File(const char* filename) { if ( !(handle = fopen(filename, "r")) ) throw std::runtime_error("blah blah blah"); } ~File() { if (handle) fclose(handle); } File(File&& that) { handle = that.handle; that.handle = nullptr; } File& operator=(File&& that) { std::swap(handle, that.handle); return *this; } File(const File&) = delete; //  void operator=(const File&) = delete; //  // ... }; 


We again prohibit normal copying, but allow the object to be moved. Now this code works:

 std::vector<File> files; files.push_back(File("data1.txt")); files.push_back(File("data2.txt")); files.erase(files.begin()); 


In addition, thanks to the variadic templates, a new template function emplace_back appeared in the containers, which allows you to create an object directly in the container without copying it:

 std::vector<File> files; files.emplace_back("data1.txt"); //  File("data1.txt")    


I hope that this note clearly shows how important the innovation is the semantics of moving objects. All success in the transition to a new standard!

Source: https://habr.com/ru/post/174019/


All Articles