Bạn nên trả về theo giá trị.
Tiêu chuẩn có một tính năng cụ thể để cải thiện hiệu quả của việc trả lại theo giá trị. Nó được gọi là "copy elision", và cụ thể hơn trong trường hợp này là "tối ưu hóa giá trị trả về được đặt tên (NRVO)".
Các trình biên dịch không phải triển khai nó, nhưng sau đó một lần nữa các trình biên dịch không phải triển khai nội tuyến hàm (hoặc thực hiện bất kỳ tối ưu hóa nào). Nhưng hiệu suất của các thư viện tiêu chuẩn có thể khá kém nếu trình biên dịch không tối ưu hóa và tất cả các trình biên dịch nghiêm túc thực hiện nội tuyến và NRVO (và các tối ưu hóa khác).
Khi NRVO được áp dụng, sẽ không có bản sao trong mã sau:
std::vector<int> f() {
std::vector<int> result;
... populate the vector ...
return result;
}
std::vector<int> myvec = f();
Nhưng người dùng có thể muốn làm điều này:
std::vector<int> myvec;
... some time later ...
myvec = f();
Copy elision không ngăn một bản sao ở đây vì nó là một nhiệm vụ chứ không phải là một khởi tạo. Tuy nhiên, bạn vẫn nên trả lại theo giá trị. Trong C ++ 11, phép gán được tối ưu hóa bằng một thứ khác, được gọi là "chuyển ngữ nghĩa". Trong C ++ 03, đoạn mã trên không gây ra một bản sao và mặc dù về lý thuyết, trình tối ưu hóa có thể tránh nó, nhưng trên thực tế, nó quá khó. Vì vậy, thay vì myvec = f()
, trong C ++ 03, bạn nên viết như sau:
std::vector<int> myvec;
... some time later ...
f().swap(myvec);
Có một tùy chọn khác, đó là cung cấp giao diện linh hoạt hơn cho người dùng:
template <typename OutputIterator> void f(OutputIterator it) {
... write elements to the iterator like this ...
*it++ = 0;
*it++ = 1;
}
Sau đó, bạn cũng có thể hỗ trợ giao diện dựa trên vectơ hiện có trên đó:
std::vector<int> f() {
std::vector<int> result;
f(std::back_inserter(result));
return result;
}
Điều này có thể kém hiệu quả hơn mã hiện tại của bạn, nếu mã hiện tại của bạn sử dụng reserve()
theo cách phức tạp hơn chỉ một số tiền cố định trả trước. Nhưng nếu mã hiện tại của bạn về cơ bản gọi push_back
nhiều lần vào vectơ, thì mã dựa trên mẫu này phải tốt như vậy.
f
?