Кратко:
Семантика перемещения (move semantics) — это механизм, позволяющий передавать ресурсы от одного объекта к другому без затрат на их копирование. Она особенно полезна в шаблонных функциях и классах, где важно оптимизировать работу с временными объектами.
Основные свойства:
- Rvalue-ссылки: используются для захвата временных объектов.
- std::move: преобразует lvalue в rvalue, позволяя вызвать конструктор или оператор перемещения.
- Конструкторы и операторы перемещения: определяют, как объект должен переместить свои ресурсы.
Примеры кода
Пример функции с семантикой перемещения
#include <utility>
#include <vector>
#include <iostream>
template <typename T>
void printVector(const std::vector<T>& vec) {
for (const T& element : vec) {
std::cout << element << " ";
}
std::cout << std::endl;
}
template<typename T>
void processVector(std::vector<T>&& vec) {
std::vector<T> localVec = std::move(vec); // Перемещаем ресурсы
// Используем localVec
std::cout << "Vector size: " << localVec.size() << std::endl;
}
int main() {
std::vector<int> myVec = {1, 2, 3, 4, 5};
printVector(myVec); // Выводится вектор
processVector(std::move(myVec)); // Передаем временный объект
printVector(myVec); // Выводится пустота
return 0;
}
Пример класса с семантикой перемещения
#include <iostream>
#include <utility>
template<typename T>
class MyContainer {
public:
T* data;
size_t size;
// Конструктор
MyContainer(size_t s) : size(s), data(new T[s]) {}
// Деструктор
~MyContainer() { delete[] data; }
// Конструктор перемещения. noexcept сообщает компилятору, что конструктор никогда не выбросит исключения
MyContainer(MyContainer&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// Оператор перемещения
MyContainer& operator=(MyContainer&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
// Запрещаем копирование
MyContainer(const MyContainer&) = delete;
MyContainer& operator=(const MyContainer&) = delete;
};
int main() {
MyContainer<int> container1(10);
MyContainer<int> container2 = std::move(container1); // Перемещаем ресурсы
return 0;
}
Особенности и рекомендации
- Используйте
std::move
только тогда, когда уверены, что больше не будете использовать исходный объект. - Определяйте конструкторы и операторы перемещения для классов, работающих с динамическими ресурсами.
- Сочетайте семантику перемещения с perfect forwarding для максимально эффективного кода в шаблонах.
Пример perfect forwarding
#include <utility>
#include <iostream>
template<typename T, typename Arg>
std::shared_ptr<T> create(Arg&& arg) {
return std::make_shared<T>(std::forward<Arg>(arg));
}
class MyClass {
public:
MyClass(int x) { std::cout << "Constructed with int: " << x << std::endl; }
MyClass(const std::string& str) { std::cout << "Constructed with string: " << str << std::endl; }
};
int main() {
auto obj1 = create<MyClass>(42); // Передаем int
auto obj2 = create<MyClass>(std::string("Hello")); // Передаем string
return 0;
}