2015-02-17 20 views
9

Hãy xem xét các chức năng sau:tuyên bố Return bất thường

Widget f(Widget w) { 
    return w; 
} 

Giả sử Widget thực hiện cả hai bản sao và di chuyển nhà thầu, theo tiêu chuẩn C++, w phải được đối xử như một rvalue đối tượng trong câu lệnh return, trong trường hợp trình biên dịch sẽ không xem xét sao chép elision một lựa chọn tốt hơn.

Mặt khác, hãy xem xét các phiên bản dưới đây:

Widget f(Widget&& w) { 
    return w; 
} 

Như đối diện với phiên bản đầu tiên, theo Item 25 của Effective Modern C++, tác giả dường như được ngụ ý rằng trở w chắc chắn sẽ gọi constructor sao chép. Nói cách khác, ông đề nghị trả lại std::move(w) thay vào đó, để làm cho trình biên dịch sử dụng hàm khởi động (có thể nhanh hơn).

bạn có thể giải thích lý do tại sao phiên bản thứ hai của f() tham gia một Widget&& như là đối số không tương đương với phiên bản đầu tiên tham gia một Widget bởi giá trị đối với các nhà xây dựng với được gọi trong báo cáo lợi nhuận, thậm chí còn nghĩ rằng trong cơ thể của cả hai các chức năng biểu thức w đề cập đến một lvalue?

dụ Complete chứng minh hành vi:

#include <iostream> 

struct Widget 
{ 
    Widget() { std::cout << "constructed" << std::endl; } 
    ~Widget() { std::cout << "destructed" << std::endl; } 
    Widget(const Widget&) { std::cout << "copy-constructed" << std::endl; } 
    Widget(Widget&&) { std::cout << "move-constructed" << std::endl; } 
}; 

Widget 
f1(Widget w) 
{ 
    return w; 
} 

Widget 
f2(Widget&& w) 
{ 
    return w; 
} 

int 
main() 
{ 
    f1(Widget {}); 
    std::cout << std::endl; 
    f2(Widget {}); 
} 

Output:

constructed 
move-constructed 
destructed 
destructed 

constructed 
copy-constructed 
destructed 
destructed 
+0

Cả hai w trong 'return w' đều là các giá trị (có thể là bản sao được trả về). Sự khác biệt chính là các đối số hàm, đầu tiên là một bản sao (!) Thứ hai không. Áp dụng một std :: di chuyển đến hàm thứ hai nên làm cho nó (gần như?) Một noop. –

+3

@MattMcNabb Nó thực sự là đúng. Có một ngoại lệ cho các tài liệu tham khảo, phải có.Làm cho 'Widget f (Widget & w) {return w; } 'di chuyển âm thầm sẽ nguy hiểm. Bây giờ, ngoại lệ đó * có thể * sẽ ổn nếu nó chỉ dành cho các tham chiếu lvalue, nhưng nó chỉ đơn giản là cho mọi thứ không được biết là an toàn để di chuyển. – hvd

+0

@hvd 'return w;' luôn sao chép đối số vào giá trị trả về - bất kể đó là 'Widget w',' Widget & w' hoặc 'Widget && w'. (Ít nhất, đó là những gì trình biên dịch của tôi đang nói với tôi) .Giá trị trả về có thể được chuyển vào đối tượng được gán cho nó; động thái đó thường được ưu tiên. –

Trả lời

5

Các tiêu chí để ngầm chuyển động được dựa trên bản sao khi sự bỏ bớt được cho phép, nhưng với một số trường hợp ngoại lệ. Sao chép elision được cho phép đối với các đối tượng địa phương không phải là tham số chức năng hoặc tham số bắt. Di chuyển ngầm định thêm các đối tượng cục bộ là các tham số hàm vào đó. Vì vậy, nó cho phép nó trong Widget f(Widget w) { return w; }, nhưng không phải trong Widget f(Widget&& w) { return w; }, trong đó w là một biến địa phương, nhưng không phải là một đối tượng địa phương.

Chuyển động hoàn toàn sẽ an toàn trong một số trường hợp hiện không được phép, kể cả trong ví dụ của bạn. Tuy nhiên, việc di chuyển ngầm được xem xét cẩn thận trên cơ sở từng trường hợp cụ thể và ví dụ của bạn chưa được xem xét hoặc chưa được xem là an toàn.

Các rất tương tự ví dụ Widget f(Widget& w) { return w; } sẽ không an toàn: một người gọi làm Widget w; f(w); không thể mong đợi w để âm thầm được trừ ra bởi trình biên dịch.

Ví dụ rất giống nhau này cho thấy mức độ nguy hiểm của nó là cho phép di chuyển ngầm, và tại sao bạn phải đánh vần nó ngay cả khi nó có vẻ hiển nhiên đối với bạn. Từ ngữ được đề cập đến "biến cục bộ", bao gồm Widget&& w, sẽ cũng bao gồm Widget& w. Sẽ tồi tệ hơn khi di chuyển ngầm ở nơi không an toàn hơn là bỏ lỡ di chuyển ngầm ở nơi an toàn, phải chắc chắn rằng từ ngữ chỉ có các trường hợp an toàn.

+0

Tôi không nghĩ rằng sao chép elision được phép khi trả về một tham số chức năng. – juanchopanza

+0

@hvd: đó là những gì tôi đang cố gắng hiểu: nếu nó "không được xem là" hoặc "không an toàn". Trong trường hợp sau, tôi muốn hiểu tại sao. Ví dụ "rất giống" rõ ràng là không an toàn. – Martin

+1

@juanchopanza Rất tiếc ... Bạn rất đúng, và câu trả lời của tôi là sai. Ngoại lệ không phải là trong bản sao chép, đó là một trường hợp bổ sung mà tiêu chuẩn đòi hỏi một động thái tiềm ẩn. Sẽ chỉnh sửa. – hvd