Nó tuyên bố một tham chiếu giá trị (tài liệu đề xuất tiêu chuẩn).
Đây là một giới thiệu về tài liệu tham khảo giá trị .
Đây là một cái nhìn sâu sắc tuyệt vời về các tài liệu tham khảo giá trị của một trong những nhà phát triển thư viện tiêu chuẩn của Microsoft .
THẬN TRỌNG: bài viết được liên kết trên MSDN ("Tài liệu tham khảo Rvalue: Các tính năng của C ++ 0x trong VC10, Phần 2") là phần giới thiệu rất rõ ràng về các tham chiếu Rvalue, nhưng đưa ra các tuyên bố về các tham chiếu Rvalue đã từng đúng trong dự thảo C ++ 11 tiêu chuẩn, nhưng không đúng với cái cuối cùng! Cụ thể, nó nói tại các điểm khác nhau mà các tham chiếu rvalue có thể liên kết với các giá trị, điều này đã từng đúng, nhưng đã được thay đổi. (Ví dụ: int x; int && rrx = x; không còn biên dịch trong GCC) - drewbarbs Jul 13 '14 lúc 16:12
Sự khác biệt lớn nhất giữa một tham chiếu C ++ 03 (hiện được gọi là tham chiếu lvalue trong C ++ 11) là nó có thể liên kết với một giá trị như tạm thời mà không cần phải là const. Vì vậy, cú pháp này là hợp pháp:
T&& r = T();
tài liệu tham khảo giá trị chủ yếu cung cấp cho sau đây:
Di chuyển ngữ nghĩa . Một hàm tạo di chuyển và toán tử gán di chuyển bây giờ có thể được xác định có tham chiếu giá trị thay vì tham chiếu const-lvalue thông thường. Một chức năng di chuyển như một bản sao, ngoại trừ nó không bắt buộc phải giữ nguồn không thay đổi; trong thực tế, nó thường sửa đổi nguồn sao cho nó không còn sở hữu các tài nguyên đã di chuyển. Điều này là tuyệt vời để loại bỏ các bản sao không liên quan, đặc biệt là trong việc triển khai thư viện tiêu chuẩn.
Ví dụ: một hàm tạo sao chép có thể trông như thế này:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Nếu nhà xây dựng này được thông qua tạm thời, bản sao sẽ không cần thiết bởi vì chúng tôi biết tạm thời sẽ bị hủy; Tại sao không sử dụng các tài nguyên tạm thời đã được phân bổ? Trong C ++ 03, không có cách nào để ngăn chặn bản sao vì chúng tôi không thể xác định chúng tôi đã được thông qua tạm thời. Trong C ++ 11, chúng ta có thể quá tải một hàm tạo di chuyển:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Lưu ý sự khác biệt lớn ở đây: hàm tạo di chuyển thực sự sửa đổi đối số của nó. Điều này sẽ "di chuyển" tạm thời vào đối tượng đang được xây dựng, do đó loại bỏ các bản sao không cần thiết.
Hàm xây dựng di chuyển sẽ được sử dụng cho tạm thời và cho các tham chiếu giá trị không phải là const được chuyển đổi rõ ràng thành tham chiếu giá trị bằng cách sử dụng std::move
hàm (nó chỉ thực hiện chuyển đổi). Đoạn mã sau gọi cả hàm tạo di chuyển cho f1
và f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Chuyển tiếp hoàn hảo . tham chiếu rvalue cho phép chúng ta chuyển tiếp các đối số đúng cho các hàm templated. Lấy ví dụ chức năng nhà máy này:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Nếu chúng ta gọi factory<foo>(5)
, đối số sẽ được suy ra int&
, sẽ không liên kết với nghĩa đen 5, ngay cả khi hàm tạo foo
của nó có một int
. Chà, thay vào đó chúng ta có thể sử dụng A1 const&
, nhưng nếu foo
lấy đối số của hàm tạo bằng tham chiếu không phải là const thì sao? Để thực hiện một chức năng nhà máy thực sự chung chung, chúng ta sẽ phải quá tải nhà máy trên A1&
và trên A1 const&
. Điều đó có thể ổn nếu nhà máy lấy 1 loại tham số, nhưng mỗi loại tham số bổ sung sẽ nhân hệ số quá tải cần thiết được đặt bằng 2. Điều đó rất nhanh không thể nhận ra.
tham chiếu rvalue khắc phục vấn đề này bằng cách cho phép thư viện chuẩn xác định một std::forward
hàm có thể chuyển tiếp các tham chiếu lvalue / rvalue đúng cách. Để biết thêm thông tin về cách làm std::forward
việc, xem câu trả lời tuyệt vời này .
Điều này cho phép chúng tôi xác định chức năng của nhà máy như thế này:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Bây giờ rvalue / lvalue-ness của đối số được giữ nguyên khi được truyền cho hàm tạo T
của đối số. Điều đó có nghĩa là nếu nhà máy được gọi với một giá trị, thì hàm tạo T
của nó được gọi với một giá trị. Nếu nhà máy được gọi với một giá trị, thì hàm tạo T
của nó được gọi với một giá trị. Chức năng nhà máy được cải thiện hoạt động nhờ một quy tắc đặc biệt:
Khi các loại tham số chức năng có dạng T&&
nơi T
là một tham số mẫu, và các tham số của hàm là một vế trái của loại A
, loại A&
được sử dụng để khấu trừ mẫu đối số.
Vì vậy, chúng ta có thể sử dụng nhà máy như vậy:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Các thuộc tính tham chiếu giá trị quan trọng :
- Đối với độ phân giải quá tải, giá trị thích liên kết với tham chiếu lvalue và giá trị thích ràng buộc với tham chiếu giá trị . Do đó, tại sao các bộ tạm thời thích gọi một hàm tạo di chuyển / toán tử gán di chuyển trên một toán tử sao chép / toán tử gán.
- tham chiếu giá trị sẽ ngầm liên kết với các giá trị và thời gian là kết quả của một chuyển đổi ngầm định . tức
float f = 0f; int&& i = f;
là được hình thành tốt bởi vì float được chuyển đổi hoàn toàn thành int; tham chiếu sẽ là tạm thời là kết quả của việc chuyển đổi.
- Tài liệu tham khảo rvalue được đặt tên là giá trị. Tài liệu tham khảo rvalue không tên là giá trị. Điều này rất quan trọng để hiểu tại sao
std::move
cuộc gọi là cần thiết trong:foo&& r = foo(); foo f = std::move(r);