1. Vấn đề nó giải quyết
std::function yêu cầu target được lưu phải copy được. C++23 thêm std::move_only_function để callback type-erased có thể sở hữu trạng thái move-only như std::unique_ptr.
2. Kiến thức cần có
Ngày 3: quyền sở hữu move-only và forwarding.
Ngày 14–15: lambda và callable object.
3. Ý tưởng cốt lõi
Đây là hộp callable có thể move nhưng không copy. Chữ ký trong dấu ngoặc nhọn quy định bên gọi được phép gọi target bên trong như thế nào. Hãy xác định giá trị đang được biến đổi rồi kiểm tra kiểu, lifetime hoặc quyền sở hữu kết quả. Bật C++23 chưa chắc thư viện đã triển khai đủ, vì vậy cần kiểm tra feature-test macro liên quan.
4. Cú pháp tối thiểu
std::move_only_function<int()> task = [p = std::move(ptr)] { return *p; };5. Cách nó hoạt động
Ví dụ tạo biểu thức hoặc đối tượng nhỏ nhất cho
std::move_only_function.Chương trình lưu lambda sở hữu unique pointer rồi gọi nó qua type erasure.
Cuối cùng, nó in hoặc kiểm tra số nguyên chỉ callback sở hữu để dễ đối chiếu.
6. Lỗi thường gặp
Copy wrapper gây lỗi biên dịch, và gọi wrapper rỗng có undefined behavior thay vì bảo đảm ném
bad_function_callnhưstd::function.Không kiểm tra feature-test macro có thể chọn nhánh mã thư viện hiện tại chưa hỗ trợ.
7. Khi nào nên dùng
Nên dùng khi callback cần type erasure lúc chạy và phải sở hữu capture không copy được.
Tránh dùng khi kiểu lambda cụ thể đã đủ hoặc bên gọi thật sự cần callback copy được.
8. Ví dụ đơn giản
Hàng đợi công việc lưu các task, mỗi task sở hữu tài nguyên khác nhau rồi chuyển từng task cho một worker. Chương trình dùng dữ liệu cố định để output dễ kiểm tra.
Mã mẫu hoàn chỉnh
Tệp mã nguồn
cpp23/28_move_only_function/main.cpp
#include <functional>
#include <iostream>
#include <memory>
#include <version>
int main() {
#if defined(__cpp_lib_move_only_function)
auto owned = std::make_unique<int>(7);
std::move_only_function<int()> task =
[value = std::move(owned)] { return *value; };
std::cout << "task=" << task() << '\n';
#else
std::cout << "move_only_function unavailable\n";
#endif
}
9. Điều cần nhớ
Tách quy tắc chuẩn khỏi mức hỗ trợ thực tế.
Làm rõ lifetime, ownership và kiểu suy luận.
Ưu tiên cú pháp ngắn, đúng ý định.
Guard tính năng chưa được hỗ trợ đồng đều.
10. Câu hỏi tự kiểm tra
Dễ — Cú pháp ngắn nhất ở Phần 4 là gì và nhiệm vụ chính của nó là gì?
Trung bình — Đọc chương trình mẫu: giá trị, kiểu hoặc nhánh nào được quan sát, và vì sao?
Khó — Điều gì xảy ra với nguồn sau khi move một
move_only_functionkhông rỗng, và precondition nào phải đúng trước khi gọi một trong hai wrapper?