2017-01-07 28 views
11
#include <string> 
#include <vector> 

using namespace std; 

auto f() 
{ 
    vector<string> coll{ "hello" }; 

    // 
    // Must I use move(coll[0]) ? 
    // 
    return coll[0]; 
} 

int main() 
{ 
    auto s = f(); 
    DoSomething(s); 
} 

Tôi biết: Nếu tôi chỉ return coll;, thì coll sẽ được đảm bảo sẽ được hoàn lại.Đối tượng phụ của đối tượng tạm thời có được đảm bảo được di chuyển khi trả lại không?

Tuy nhiên, tôi không chắc chắn: Liệu coll[0] cũng được đảm bảo sẽ được chuyển khi bạn quay lại?

Cập nhật:

#include <iostream> 

struct A 
{ 
    A() { std::cout << "constructed\n"; } 
    A(const A&) { std::cout << "copy-constructed\n"; } 
    A(A&&) { std::cout << "move-constructed\n"; } 
    ~A() { std::cout << "destructed\n"; } 
}; 

struct B 
{ 
    A a; 
}; 

A f() 
{ 
    B b; 
    return b.a; 
} 

int main() 
{ 
    f(); 
} 

gcc 6.2 và kêu vang 3,8 kết quả đầu ra như nhau:

xây dựng

copy-xây dựng

destructed

destructed

+2

"sau đó' coll' được đảm bảo sẽ được di chuyển khi trả lại ". Không, không phải vậy. Bản sao có thể được elided, trong trường hợp đó, không có di chuyển. – juanchopanza

+1

Bạn không sử dụng giá trị trả về của f() để có gì để di chuyển? – Surt

+0

Và các điều kiện mà theo đó một giá trị có thể được di chuyển được kết hợp chặt chẽ để sao chép elision (xem câu trả lời của tôi.) – juanchopanza

Trả lời

3

Việc xây dựng sạch của "ngầm di chuyển" quy tắc là trong [class.copy.elision]/3 của giấy làm việc hiện tại:

Trong bối cảnh sao chép khởi sau, một hoạt động di chuyển có thể được sử dụng thay cho một hoạt động sao chép:

  • Nếu biểu hiện trong câu lệnh trả về ([stmt.trở lại]) là một (có thể ngoặc) id-biểu rằng tên một đối tượng với thời gian lưu trữ tự động khai báo trong cơ thể hoặc tham số-khai-khoản của hàm trong cùng kèm theo hoặc lambda-biểu, hoặc

  • [...]

độ phân giải quá tải để chọn các nhà xây dựng cho các bản sao là lần đầu tiên thực hiện như nếu đối tượng đã được chỉ định bởi một rvalue. Nếu độ phân giải quá tải đầu tiên không thành công hoặc không được thực hiện hoặc nếu loại tham số đầu tiên của nhà xây dựng đã chọn không phải là tham chiếu giá trị đến loại đối tượng (có thể cv đủ điều kiện), độ phân giải quá tải là thực hiện lại, xem xét đối tượng như một giá trị.

Cả b.a cũng không coll[0] là một id-biểu. Do đó, không có động thái tiềm ẩn. Nếu bạn muốn di chuyển, bạn sẽ phải làm điều đó một cách rõ ràng.

4

Khi trả về một đối tượng địa phương, không phải là một bản sao cũng không phải là thái này sẽ được sử dụng, nhưng sao chép sự bỏ bớt, mà là để được ưa thích hơn di chuyển. Điều này là do các quy tắc quản lý việc sao chép bỏ qua và di chuyển các đối tượng địa phương là như nhau. Khi thay vì buộc một động thái bằng cách sử dụng một cách rõ ràng std::move như trong

template<typename T> 
std::string make_string(T const& x) 
{ 
    std::ostringstream str; 
    str << x 
    return std::move(str.str()); // not recommended 
} 

phiên bản gần đây về vấn đề kêu vang một cảnh báo

di chuyển một đối tượng ngăn chặn tạm thời sao chép sự bỏ bớt [-Wpessimizing-di chuyển]

Tuy nhiên, tình huống trong mã của bạn khác nhau. Không giống như std::ostringstream::str(), trả về một đối tượng (std::string), std::vector<>::operator[], trả về một tham chiếu, mà phải được chuyển đổi thành một đối tượng (vì auto xóa tham chiếu). Trong trường hợp này, không thể sao chép elision (vì đối tượng thực sự là một phần của đối tượng khác với destructor không tầm thường) và std::move() nên được sử dụng để tránh một bản sao.

Những cân nhắc này đề xuất sử dụng std::move() nếu không chắc chắn, nhưng loại bỏ nếu clang gặp sự cố ở trên.

+0

Không có gcc 6.2 và clang 3.8 thực hiện việc sao chép bản sao trong ví dụ ban đầu của tôi. Xem cập nhật của tôi. – xmllmx

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