Tôi đoán là không, nhưng tôi xin xác nhận. Có bất kỳ sử dụng cho const Foo&&
, ở đâu Foo
một loại lớp?
Tôi đoán là không, nhưng tôi xin xác nhận. Có bất kỳ sử dụng cho const Foo&&
, ở đâu Foo
một loại lớp?
Câu trả lời:
Chúng đôi khi hữu ích. Bản thân dự thảo C ++ 0x sử dụng chúng ở một số nơi, ví dụ:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Hai quá tải trên đảm bảo rằng các hàm khác ref(T&)
và các cref(const T&)
hàm không liên kết với các giá trị (nếu không sẽ có thể).
Cập nhật
Tôi vừa kiểm tra tiêu chuẩn chính thức N3290 , rất tiếc là tiêu chuẩn này không được cung cấp công khai và nó có trong 20.8 Đối tượng chức năng [function.objects] / p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Sau đó, tôi đã kiểm tra bản nháp sau C ++ 11 gần đây nhất, có sẵn công khai, N3485 và trong 20.8 Đối tượng chức năng [function.objects] / p2, nó vẫn cho biết:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
const T&&
được sử dụng?
const T&&
ngăn chặn ai đó sử dụng một cách ngu ngốc các args mẫu rõ ràng của biểu mẫu ref<const A&>(...)
. Đó không phải là một lập luận quá mạnh mẽ, nhưng chi phí của việc const T&&
vượt qua T&&
là khá nhỏ.
Ngữ nghĩa của việc nhận tham chiếu const rvalue (và không phải cho =delete
) là để nói:
Trường hợp sử dụng sau đây có thể là IMHO một trường hợp sử dụng tốt để tham chiếu giá trị tới const , mặc dù ngôn ngữ đã quyết định không sử dụng cách tiếp cận này (xem bài đăng gốc SO ).
Nó thường được khuyến khích sử dụng make_unique
và make_shared
, nhưng cả hai unique_ptr
và đều shared_ptr
có thể được xây dựng từ một con trỏ thô. Cả hai hàm tạo đều lấy con trỏ theo giá trị và sao chép nó. Cả hai đều cho phép (nghĩa là: không ngăn cản ) việc sử dụng liên tục con trỏ ban đầu được truyền cho chúng trong hàm tạo.
Các biên dịch mã sau và kết quả với miễn phí gấp đôi :
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Cả hai unique_ptr
và đều shared_ptr
có thể ngăn chặn điều trên nếu các hàm tạo có liên quan của chúng mong đợi nhận được con trỏ thô dưới dạng const rvalue , ví dụ: for unique_ptr
:
unique_ptr(T* const&& p) : ptr{p} {}
Trong trường hợp đó, đoạn mã miễn phí kép ở trên sẽ không biên dịch, nhưng đoạn mã sau sẽ:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Lưu ý rằng nó ptr
vẫn có thể được sử dụng sau khi nó được di chuyển, vì vậy lỗi tiềm ẩn không hoàn toàn biến mất. Nhưng nếu người dùng được yêu cầu gọi std::move
một lỗi như vậy sẽ rơi vào quy tắc chung là: không sử dụng tài nguyên đã được di chuyển.
Người ta có thể hỏi: OK, nhưng tại sao lại là T*
const&& p
?
Lý do rất đơn giản, để cho phép tạo unique_ptr
từ con trỏ const . Hãy nhớ rằng tham chiếu const rvalue chung chung hơn là chỉ tham chiếu rvalue vì nó chấp nhận cả const
và non-const
. Vì vậy, chúng tôi có thể cho phép những điều sau:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
điều này sẽ không xảy ra nếu chúng ta chỉ mong đợi tham chiếu rvalue (lỗi biên dịch: không thể ràng buộc const rvalue với rvalue ).
Nhưng dù sao, điều này là quá muộn để đề xuất một điều như vậy. Nhưng ý tưởng này trình bày một cách sử dụng hợp lý tham chiếu rvalue đến const .
Chúng được cho phép và thậm chí là các hàm được xếp hạng dựa trên const
, nhưng vì bạn không thể di chuyển từ đối tượng const được giới thiệu const Foo&&
nên chúng không hữu ích.
const T&, T&, const T&&, T&&
Bên cạnh std :: ref , thư viện chuẩn cũng sử dụng tham chiếu const rvalue trong std :: as_const cho cùng mục đích.
template <class T>
void as_const(const T&&) = delete;
Nó cũng được sử dụng làm giá trị trả về trong std :: tùy chọn khi nhận giá trị được bọc:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
Cũng như trong std :: get :
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
Điều này có lẽ là để duy trì loại giá trị cũng như tính không đổi của trình bao bọc khi truy cập giá trị được bao bọc.
Điều này tạo ra sự khác biệt liệu các hàm đủ điều kiện ref const rvalue có thể được gọi trên đối tượng được bọc hay không. Điều đó nói rằng, tôi không biết bất kỳ cách sử dụng nào cho các hàm đủ điều kiện của const rvalue ref.
Tôi không thể nghĩ đến tình huống mà điều này sẽ hữu ích trực tiếp, nhưng nó có thể được sử dụng gián tiếp:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
T trong g là T const, vì vậy f 's x là T const &&.
Có khả năng điều này dẫn đến lỗi comile trong f (khi nó cố gắng di chuyển hoặc sử dụng đối tượng), nhưng f có thể nhận một rvalue-ref để nó không thể được gọi trên các giá trị, mà không cần sửa đổi giá trị (như trong quá trình đơn giản ví dụ trên).
const&&
là rất quan trọng, mặc dù ông không nói lý do tại sao: youtube.com/watch?v=JhgWFYfdIho#t=54m20s