2012-03-02 37 views
21

Tôi biết rằng đó là bình thường không phải là một ý tưởng tốt để trở lại với std::move, ví dụ:Quay lại với `std :: move` hợp lý trong trường hợp có nhiều câu lệnh trả về?

bigObject foo() { bigObject result; /*...*/ return std::move(result); } 

thay vì chỉ đơn giản là

bigObject foo() { bigObject result; /*...*/ return result; } 

vì nó được theo cách tối ưu hóa giá trị trả về. Nhưng những gì trong trường hợp của một hàm với nhiều lợi nhuận khác nhau, đặc biệt là một cái gì đó giống như

class bar { 
    bigObject fixed_ret; 
    bool use_fixed_ret; 
    void prepare_object(bigObject&); 
public: 
    bigObject foo() { 
    if(use_fixed_ret) 
     return fixed_ret; 
    else{ 
     bigObject result; 
     prepare_object(result); 
     return result; 
    } 
    } 
}; 

Tôi nghĩ bình thường tối ưu hóa giá trị trả về là không thể trong một chức năng như vậy, vì vậy nó sẽ là một ý tưởng tốt để đưa vào

 return std::move(result); 

đây, hay tôi nên thay vì làm (IMO xấu xí, nhưng đó là gây tranh cãi)

bigObject foo() { 
    bigObject result; 
    if(use_fixed_ret) 
     result = fixed_ret; 
    else{ 
     prepare_object(result); 
    } 
    return result; 
    } 
+0

Tôi không thể tham khảo tiêu chuẩn để tôi không trả lời, nhưng tôi chắc chắn rằng bạn không cần std :: di chuyển, tôi nghĩ bạn đang nhầm lẫn RVO và sao chép elision, sao chép elision là tối ưu hóa lợi ích trên một số các trình biên dịch có một đường dẫn trả về đơn. BigObject sẽ trở thành một giá trị R liên quan đến nơi nó trở về. – 111111

+0

Vâng, tôi nghĩ rằng sao chép elision là những gì tôi có nghĩa là trong đầu. – leftaroundabout

+1

Ngoài ra, nếu bạn thích tối ưu hóa mã và mức mã, bạn có vẻ như, :), bạn có thể loại bỏ nhánh 'else {...' khác trong hàm 'foo()' của bạn. Như thể câu lệnh đầu tiên là đúng thì câu thứ hai sẽ không được đánh giá. – 111111

Trả lời

33

Đối với các biến địa phương, không có cần phải std::move chúng trong return tuyên bố phần lớn thời gian , vì ngôn ngữ thực sự đòi hỏi rằng điều này xảy ra tự động:

§12.8 [class.copy] p32

Khi các tiêu chuẩn cho sự bỏ bớt một hoạt động sao chép được đáp ứng hoặc sẽ được đáp ứng tiết kiệm cho thực tế rằng đối tượng nguồn là một tham số hàm, và đối tượng được sao chép được chỉ định bởi một giá trị, độ phân giải quá tải để chọn hàm tạo cho bản sao được thực hiện đầu tiên như thể đối tượng được chỉ định bởi giá trị. Nếu độ phân giải quá tải không thành công, hoặc nếu kiểu tham số đầu tiên của hàm tạo đã chọn không phải là tham chiếu rvalue đối với loại đối tượng (có thể cv đủ điều kiện), thì độ phân giải quá tải sẽ được thực hiện lại, xem xét đối tượng dưới dạng giá trị. [Lưu ý: Độ phân giải quá tải hai giai đoạn này phải được thực hiện bất kể việc quét bản sao có xảy ra hay không. Nó xác định constructor được gọi nếu không thực hiện elision, và constructor đã chọn phải được truy cập ngay cả khi cuộc gọi được elided. -end lưu ý]


† Sao chép sự bỏ bớt được rất hạn chế ở nơi nó có thể được áp dụng (§12.8/31). Một hạn chế như vậy là kiểu đối tượng nguồn phải giống với kiểu trả về cv-unqualified của hàm khi xử lý câu lệnh trả về. Nó cũng không áp dụng cho các subobject của các biến cục bộ sắp ra khỏi phạm vi.

+2

Và đây là tài liệu tham khảo tiêu chuẩn của bạn. – 111111

+1

Được rồi ... Tôi luôn gặp khó khăn với các công thức trong tiêu chuẩn, đọc nó năm lần và vẫn không hiểu ý nghĩa của nó. Nhưng bạn nói, trong ví dụ của tôi, nó chỉ đơn giản có nghĩa là "phiên bản đầu tiên là tốt như nó được"? Sau đó, tôi sẽ tin tưởng bạn ở đó. – leftaroundabout

+2

@leftaroundabout: Về cơ bản nó nói "Nếu đối tượng là cục bộ và được trả về theo giá trị, trước tiên hãy thử trả về giá trị như một giá trị (như' std :: move' sẽ làm). Nếu không tìm thấy một ctor di chuyển, cố gắng giống nhau nhưng lần này là một giá trị – Xeo

Các vấn đề liên quan