Dường như có sự nhầm lẫn về cách hoạt động của RVO (Tối ưu hóa giá trị trả lại).
Một ví dụ đơn giản:
#include <iostream>
struct A {
int a;
int b;
int c;
int d;
};
A create(int i) {
A a = {i, i+1, i+2, i+3 };
std::cout << &a << "\n";
return a;
}
int main(int argc, char*[]) {
A a = create(argc);
std::cout << &a << "\n";
}
Và sản lượng của nó tại ideone:
0xbf928684
0xbf928684
ngạc nhiên?
Trên thực tế, đó là ảnh hưởng của RVO: đối tượng để được trả lại được xây dựng trực tiếp tại chỗ trong người gọi.
Làm cách nào?
Theo truyền thống, người gọi (main
tại đây) sẽ đặt trước một số khoảng trống trên giá trị trả lại: khe trả về; callee (create
ở đây) được chuyển (bằng cách nào đó) địa chỉ của khe trả về để sao chép giá trị trả về của nó vào. Sau đó, callee phân bổ không gian riêng của nó cho biến cục bộ, trong đó nó tạo kết quả, giống như cho bất kỳ biến cục bộ nào khác, và sau đó sao chép nó vào khe trả về sau câu lệnh return
.
RVO được kích hoạt khi trình biên dịch suy ra từ mã mà biến có thể được xây dựng trực tiếp vào khe trả về với ngữ nghĩa tương đương (quy tắc as-if). Lưu ý rằng đây là một tối ưu hóa phổ biến mà nó được liệt kê rõ ràng bởi tiêu chuẩn và trình biên dịch không phải lo lắng về các tác dụng phụ có thể xảy ra của nhà xây dựng bản sao (hoặc di chuyển).
Khi nào?
Trình biên dịch rất có thể sử dụng quy tắc đơn giản, chẳng hạn như:
// 1. works
A unnamed() { return {1, 2, 3, 4}; }
// 2. works
A unique_named() {
A a = {1, 2, 3, 4};
return a;
}
// 3. works
A mixed_unnamed_named(bool b) {
if (b) { return {1, 2, 3, 4}; }
A a = {1, 2, 3, 4};
return a;
}
// 4. does not work
A mixed_named_unnamed(bool b) {
A a = {1, 2, 3, 4};
if (b) { return {4, 3, 2, 1}; }
return a;
}
Trong trường hợp sau (4), tối ưu hóa không thể được áp dụng khi A
được trả về bởi vì trình biên dịch không thể xây dựng a
trong trở lại khe, vì nó có thể cần nó cho cái gì khác (tùy thuộc vào điều kiện boolean b
).
Một nguyên tắc đơn giản của ngón tay cái là như vậy đó:
RVO nên được áp dụng nếu không có ứng cử viên khác cho khe trở lại đã được công bố trước khi báo cáo kết quả return
.
di chuyển ngữ nghĩa: http://www2.research.att.com/~bs/C++0xFAQ.html#rval – chris
Nó không nhất thiết phải tạo bản sao. NRVO hoặc di chuyển symantics có thể ngăn chặn điều đó. –
Bạn có thể dựa vào trình biên dịch của mình để thực hiện phép thuật NRVO hoặc sử dụng ngữ nghĩa di chuyển một cách dễ dàng. –