Hãy xem xét các trường hợp đơn giản nhất:
lofactory(...).some_method();
Trong trường hợp này một bản sao từ lofactory đến ngữ cảnh người gọi là có thể – nhưng nó có thể được tối ưu hóa bằng cách RVO/NRVO.
LargeObject mylo2 (lofactory(...));
Trong trường hợp này bản sao có thể là:
- Return tạm từ lofactory đến người gọi bối cảnh – có thể được tối ưu hóa đi RVO/NRVO
- Copy- xây dựng mylo2 từ tạm – có thể được tối ưu hóa đi sao chép sự bỏ bớt
const LargeObject& mylo1 = lofactory(...);
Trong trường hợp này, có một bản sao vẫn còn có thể:
- Return tạm từ lofactory đến ngữ cảnh người gọi – có thể được tối ưu hóa awa y by RVO/NRVO (quá!)
Một tham chiếu sẽ liên kết với điều này tạm thời.
Vì vậy,
Có bất kỳ quy tắc chung được chấp nhận hoặc thực hành tốt nhất khi sử dụng const & để temporaries và khi nào thì dựa vào RVO/NRVO?
Như tôi đã nói ở trên, ngay cả trong một trường hợp với const&
, một bản sao unnecesary là có thể, và nó có thể được tối ưu hóa đi RVO/NRVO.
Nếu trình biên dịch của bạn áp dụng RVO/NVRO trong một số trường hợp, thì rất có khả năng nó sẽ thực hiện copy-elision ở giai đoạn 2 (ở trên). Bởi vì trong trường hợp đó, copy-elision đơn giản hơn nhiều so với NRVO.
Nhưng, trong trường hợp xấu nhất, bạn sẽ có một bản sao cho trường hợp const&
và hai bản sao khi bạn bắt đầu giá trị.
Có thể có tình huống trong đó sử dụng phương pháp const & tệ hơn không sử dụng?
Tôi không nghĩ rằng có những trường hợp như vậy. Ít nhất trừ khi trình biên dịch của bạn sử dụng các quy tắc lạ phân biệt const&
. (Ví dụ về một tình huống tương tự, tôi nhận thấy rằng MSVC không làm NVRO để khởi tạo tổng hợp.)
(Tôi đang nghĩ ví dụ về C++ 11 ngữ nghĩa di chuyển nếu LargeObject có những ngữ nghĩa được triển khai ...)
trong C++ 11, nếu LargeObject
có di chuyển ngữ nghĩa, sau đó trong trường hợp xấu nhất, bạn sẽ có một động thái đối với trường hợp const&
, và hai di chuyển khi bạn init giá trị. Vì vậy, const&
vẫn còn tốt hơn một chút.
Vì vậy, một quy tắc tốt sẽ luôn luôn ràng buộc là tạm thời để const & nếu có thể, vì nó có thể ngăn chặn một bản sao nếu trình biên dịch không thực hiện một bản sao-sự bỏ bớt vì một lý do?
Không biết bối cảnh ứng dụng thực tế, điều này có vẻ như là một quy tắc tốt.
Trong C++ 11, bạn có thể liên kết tham chiếu tạm thời với rvalue - LargeObject & &. Vì vậy, tạm thời như vậy có thể được sửa đổi.
Bằng cách này, hãy chuyển mô phỏng ngữ nghĩa có sẵn cho C++ 98/03 bằng các thủ thuật khác nhau. Ví dụ:
Tuy nhiên, ngay cả khi có ngữ nghĩa di chuyển - có các đối tượng không thể di chuyển rẻ. Ví dụ, lớp ma trận 4x4 với dữ liệu kép [4] [4] bên trong. Vì vậy, Copy-elision RVO/NRVO vẫn rất quan trọng, ngay cả trong C++ 11. Và bằng cách này, khi Copy-elision/RVO/NRVO xảy ra - nó nhanh hơn di chuyển.
PS, trong những trường hợp thực tế, có một số điều bổ sung mà cần được xem xét:
Ví dụ, nếu bạn có hàm trả về vector, ngay cả khi di chuyển/RVO/NRVO/Copy-sự bỏ bớt sẽ được áp dụng - nó vẫn có thể không hiệu quả 100%. Ví dụ, hãy xem xét trường hợp sau đây:
while(/*...*/)
{
vector<some> v = produce_next(/* ... */); // Move/RVO/NRVO are applied
// ...
}
Nó sẽ hiệu quả hơn để thay đổi mã để:
vector<some> v;
while(/*...*/)
{
v.clear();
produce_next(v); // fill v
// or something like:
produce_next(back_inserter(v));
// ...
}
Bởi vì trong trường hợp này, bộ nhớ đã được phân bổ trong vector có thể được tái sử dụng khi v.capacity() là đủ, mà không cần phải phân bổ mới bên trong produce_next trên mỗi lần lặp.
Vì vậy, một quy tắc tốt sẽ là _always_ ràng buộc thời gian để 'const &' nếu có thể, vì nó có thể ngăn chặn một bản sao nếu trình biên dịch không thực hiện một bản sao-elision vì lý do nào? –
Chấp nhận nó, cảm ơn bạn! –