std::reference_wrapper
hữu ích khi kết hợp với các mẫu. Nó bao bọc một đối tượng bằng cách lưu trữ một con trỏ tới nó, cho phép gán lại và sao chép trong khi bắt chước ngữ nghĩa thông thường của nó. Nó cũng hướng dẫn các mẫu thư viện nhất định để lưu trữ các tham chiếu thay vì các đối tượng.
Hãy xem xét các thuật toán trong STL sao chép bộ chức năng: Bạn có thể tránh sao chép đó bằng cách chỉ cần chuyển một trình bao bọc tham chiếu tham chiếu đến bộ chức năng thay vì chính bộ chức năng:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
Điều này hoạt động vì…
... reference_wrapper
s quá tảioperator()
vì vậy họ có thể được gọi giống như chức năng các đối tượng họ đề cập đến:
std::ref(myEngine)() // Valid expression, modifies myEngines state
… (Un) giống như các tham chiếu thông thường, sao chép (và gán) reference_wrappers
chỉ chỉ định người được chỉ định.
int i, j;
auto r = std::ref(i); // r refers to i
r = std::ref(j); // Okay; r refers to j
r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
Việc sao chép một trình bao bọc tham chiếu thực tế tương đương với việc sao chép một con trỏ, nó rẻ như cho. Tất cả các lệnh gọi hàm vốn có trong việc sử dụng nó (ví dụ: các lệnh gọi đến operator()
) chỉ nên được nội dòng vì chúng là một chữ lót.
reference_wrapper
s được tạo qua std::ref
vàstd::cref
:
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>
Đối số mẫu chỉ định loại và trình độ cv của đối tượng được tham chiếu đến; r2
đề cập đến một const int
và sẽ chỉ mang lại một tham chiếu đến const int
. Các lệnh gọi đến các trình bao bọc tham chiếu với các chức năng const
trong đó sẽ chỉ gọi const
hàm thành viên operator()
s.
Các trình khởi tạo Rvalue không được phép, vì cho phép chúng sẽ gây hại nhiều hơn lợi. Vì các giá trị sẽ được di chuyển bằng mọi cách (và với việc tách bản sao được đảm bảo ngay cả khi điều đó được tránh một phần), chúng tôi không cải thiện ngữ nghĩa; chúng ta có thể giới thiệu các con trỏ treo lơ lửng, vì một trình bao bọc tham chiếu không kéo dài thời gian tồn tại của con trỏ.
Thư viện tương tác
Như đã đề cập trước đây, người ta có thể hướng dẫn make_tuple
lưu trữ một tham chiếu trong kết quả tuple
bằng cách chuyển đối số tương ứng thông qua reference_wrapper
:
int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>
Lưu ý rằng điều này hơi khác với forward_as_tuple
: Ở đây, không cho phép các giá trị làm đối số.
std::bind
hiển thị cùng một hành vi: Nó sẽ không sao chép đối số nhưng lưu trữ một tham chiếu nếu nó là một reference_wrapper
. Hữu ích nếu đối số đó (hoặc functor!) Không cần được sao chép nhưng vẫn ở trong phạm vi trong khi bind
-functor được sử dụng.
Sự khác biệt so với con trỏ thông thường
Không có cấp độ bổ sung của hướng cú pháp. Con trỏ phải được tham chiếu để có được giá trị cho đối tượng mà chúng tham chiếu đến; reference_wrapper
s có một toán tử chuyển đổi ngầm định và có thể được gọi giống như đối tượng mà chúng bao bọc.
int i;
int& ref = std::ref(i); // Okay
reference_wrapper
s, không giống như con trỏ, không có trạng thái null. Chúng phải được khởi tạo bằng tham chiếu hoặc tham chiếu khácreference_wrapper
.
std::reference_wrapper<int> r; // Invalid
Một điểm tương đồng là ngữ nghĩa sao chép nông: Các con trỏ và reference_wrapper
s có thể được gán lại.
.
thay vì->