Chuyển tới nội dung chính

std::move

#include <algorithm>

template <class InputIterator, class OutputIterator>
OutputIterator move (InputIterator first, InputIterator last, OutputIterator result);

Di chuyển các phần tử từ một phạm vi (range) nguồn sang một phạm vi đích khác, sử dụng move semantics (ngữ nghĩa di chuyển) của C++11.

Tham số

first

  • Input Iterator trỏ đến phần tử đầu tiên trong phạm vi nguồn cần di chuyển.

last

  • Input Iterator trỏ đến phần tử ngay sau phần tử cuối cùng trong phạm vi nguồn. Phạm vi nguồn là [first, last).

result

  • Output Iterator trỏ đến phần tử đầu tiên trong phạm vi đích nơi các phần tử sẽ được di chuyển đến.

Giá trị trả về

  • Output Iterator trỏ đến phần tử ngay sau phần tử cuối cùng được di chuyển trong phạm vi đích.

Đặc điểm

  1. Phạm vi nguồn [first, last) là "nửa mở", không bao gồm phần tử last.
  2. Phạm vi đích phải có đủ không gian để chứa các phần tử được di chuyển.
  3. move() không đảm bảo rằng các phần tử trong phạm vi nguồn sẽ bị hủy. Nó chỉ chuyển quyền sở hữu tài nguyên.
  4. Sau khi move(), các phần tử trong phạm vi nguồn ở trạng thái hợp lệ nhưng không xác định. Bạn không nên truy cập giá trị của chúng.
  5. move() hiệu quả hơn copy() khi làm việc với các đối tượng lớn hoặc có tài nguyên đắt đỏ để sao chép.
  6. move() thường được sử dụng khi:
    • Bạn muốn chuyển quyền sở hữu tài nguyên (bộ nhớ, file handles,...) từ các đối tượng trong phạm vi nguồn sang các đối tượng trong phạm vi đích một cách hiệu quả.
    • Bạn muốn tránh việc sao chép tốn kém khi các đối tượng không còn được sử dụng trong phạm vi nguồn nữa.
    • Bạn đang làm việc với các đối tượng chỉ có thể di chuyển (move-only), không thể sao chép (ví dụ: std::unique_ptr).
  7. Move Semantics (Ngữ nghĩa di chuyển):
    • move() tận dụng move semantics của C++11. Thay vì tạo ra các bản sao sâu (deep copy) của các đối tượng, move() chuyển tài nguyên từ đối tượng nguồn sang đối tượng đích.
    • Sau khi di chuyển, đối tượng nguồn sẽ ở trong trạng thái hợp lệ nhưng không xác định (valid but unspecified state). Bạn không nên sử dụng lại đối tượng nguồn trừ khi bạn gán cho nó một giá trị mới.
    • Để một kiểu dữ liệu hỗ trợ move semantics, nó cần phải có move constructor (hàm khởi tạo di chuyển) và move assignment operator (toán tử gán di chuyển).

Ví dụ

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <utility>

int main() {
std::vector<std::string> source = {"apple", "banana", "orange"};
std::vector<std::string> destination(3);

// Di chuyển các phần tử từ source sang destination
std::move(source.begin(), source.end(), destination.begin());

std::cout << "destination after move: ";
for (const std::string& s : destination) {
std::cout << s << " ";
}
std::cout << std::endl;

std::cout << "source after move: ";
for (const std::string& s : source) {
std::cout << s << " "; // Có thể chuỗi rỗng hoặc giá trị khác
}
std::cout << std::endl;

// Sử dụng move với unique_ptr
std::vector<std::unique_ptr<int>> ptrSource;
ptrSource.push_back(std::make_unique<int>(10));
ptrSource.push_back(std::make_unique<int>(20));
ptrSource.push_back(std::make_unique<int>(30));

std::vector<std::unique_ptr<int>> ptrDestination;

std::move(ptrSource.begin(), ptrSource.end(), std::back_inserter(ptrDestination));

std::cout << "ptrDestination after move: ";
for (const auto& ptr : ptrDestination) {
if (ptr) {
std::cout << *ptr << " ";
} else {
std::cout << "nullptr ";
}
}
std::cout << std::endl;

std::cout << "ptrSource after move: ";
for (const auto& ptr : ptrSource) {
if (ptr) {
std::cout << *ptr << " ";
} else {
std::cout << "nullptr ";
}
}
std::cout << std::endl;

return 0;
}

Các hàm liên quan

move_backwardDi chuyển các phần tử từ một phạm vi (range) nguồn sang một phạm vi đích khác
copySao chép các phần tử từ một phạm vi (range) nguồn sang một phạm vi đích khác