Эта небольшая заметка о том, как с приходом нового стандарта C++11 изменились требования стандартных контейнеров к своим элементам. В C++98 от элемента контейнера требовалось, по сути, наличие «разумных» конструктора копирования и оператора присваивания. Если, например, объект вашего класса владеет каким-либо ресурсом, копирование обычно становится невозможным (по крайней мере, без «глубокого» копирования ресурса). В качестве примера давайте рассмотрим следующий класс-обертку вокруг
Мы запретили копирование и присваивание объектов этого класса, поскольку копирование
Что же делать, если требуется хранить целый список объектов типа
Типичным решением такой проблемы в C++98 является использование
Такое решение не особо радует глаз, особенно учитывая то, что мы используем динамическую память там, где, казалось бы, она не нужна.
Если мы разрешаем использование C++11, то картина сильно меняется. С появлением move semantics, стандартные контейнеры больше не требуют наличия обычных конструктора копирования и оператора присваивания, если только вы не собираетесь копировать контейнер целиком. Вместо них достаточно наличия семантики перемещения. Давайте посмотрим, как мы можем переписать пример с классом
Мы снова запрещаем обычное копирование, но разрешаем перемещение объекта. Теперь такой код работает:
Кроме того, благодаря variadic templates, в контейнерах появилась новая шаблонная функция
Надеюсь, что данная заметка наглядно показывает, насколько важным нововведением является семантика перемещения объектов. Всем успехов в переходе на новый стандарт!
FILE*, написанную на 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&); //запретить присваивание };
Мы запретили копирование и присваивание объектов этого класса, поскольку копирование
FILE* потребовало бы некоторых платформо-зависимых ухищрений, и вообще не имеет особого физического смысла.Что же делать, если требуется хранить целый список объектов типа
File? К сожалению, мы не можем использовать File в стандартном контейнере, то есть такой код просто не скомпилируется:std::vector<File> files; files.push_back(File("data.txt"));
Типичным решением такой проблемы в C++98 является использование
shared_ptr:std::vector<boost::shared_ptr<File> > files; files.push_back(boost::shared_ptr<File>(new File("data.txt")) );
Такое решение не особо радует глаз, особенно учитывая то, что мы используем динамическую память там, где, казалось бы, она не нужна.
Если мы разрешаем использование C++11, то картина сильно меняется. С появлением move semantics, стандартные контейнеры больше не требуют наличия обычных конструктора копирования и оператора присваивания, если только вы не собираетесь копировать контейнер целиком. Вместо них достаточно наличия семантики перемещения. Давайте посмотрим, как мы можем переписать пример с классом
File на 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; //запретить присваивание // ... };
Мы снова запрещаем обычное копирование, но разрешаем перемещение объекта. Теперь такой код работает:
std::vector<File> files; files.push_back(File("data1.txt")); files.push_back(File("data2.txt")); files.erase(files.begin());
Кроме того, благодаря variadic templates, в контейнерах появилась новая шаблонная функция
emplace_back, которая позволяет создать объект прямо в контейнере, не копируя его:std::vector<File> files; files.emplace_back("data1.txt"); // добавить File("data1.txt") в конец массива
Надеюсь, что данная заметка наглядно показывает, насколько важным нововведением является семантика перемещения объектов. Всем успехов в переходе на новый стандарт!
