Hiệu quả của C ++ 11 push_back () với std :: move so với emplace_back () cho các đối tượng đã được xây dựng


86

Trong C ++, 11 emplace_back()thường được ưa thích (về mặt hiệu quả) push_back()vì nó cho phép xây dựng tại chỗ, nhưng điều này có còn xảy ra khi sử dụng push_back(std::move())với một đối tượng đã được xây dựng không?

Ví dụ, emplace_back()vẫn được ưu tiên trong các trường hợp như sau?

std::string mystring("hello world");
std::vector<std::string> myvector;

myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)

Ngoài ra, có bất kỳ lợi ích nào trong ví dụ trên nếu thay vào đó làm:

myvector.emplace_back(std::move(mystring));

hay động thái ở đây là hoàn toàn dư thừa, hoặc không có tác dụng?


myvector.emplace_back(mystring);sao chép và không di chuyển. Hai động thái còn lại và nên tương đương.
TC

Câu trả lời:


116

Hãy xem các lệnh gọi khác nhau mà bạn đã cung cấp có tác dụng gì:

  1. emplace_back(mystring): Đây là một cấu trúc tại chỗ của phần tử mới với bất kỳ đối số nào bạn đã cung cấp. Vì bạn đã cung cấp một lvalue, nên cấu trúc tại chỗ đó trên thực tế là cấu trúc sao chép, tức là điều này cũng giống như cách gọipush_back(mystring)

  2. push_back(std::move(mystring)): Điều này gọi là di chuyển chèn, trong trường hợp của std :: string là một công trình di chuyển tại chỗ.

  3. emplace_back(std::move(mystring)): Đây lại là một cấu trúc tại chỗ với các đối số bạn đã cung cấp. Vì đối số đó là một rvalue, nó gọi hàm tạo di chuyển của std::string, tức là nó là một cấu trúc di chuyển tại chỗ như trong 2.

Nói cách khác, nếu được gọi với một đối số kiểu T, có thể là giá trị rvalue hoặc lvalue emplace_backpush_backtương đương.

Tuy nhiên, đối với bất kỳ (các) đối số nào khác, emplace_backsẽ thắng cuộc đua, ví dụ: với một char const*trong một vector<string>:

  1. emplace_back("foo")kêu gọi string::string(char const*)xây dựng tại chỗ.

  2. push_back("foo")trước tiên phải gọi string::string(char const*)chuyển đổi ngầm cần thiết để khớp với chữ ký của hàm, và sau đó là di chuyển chèn như trường hợp 2. ở trên. Do đó nó tương đương vớipush_back(string("foo"))


1
Hàm tạo Move thường hiệu quả hơn các hàm tạo sao chép, do đó sử dụng rvalue (trường hợp 2, 3) sẽ hiệu quả hơn sử dụng giá trị l (trường hợp 1) mặc dù cùng ngữ nghĩa.
Ryan Li

còn tình huống như vậy thì sao? void foo (string && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
VALOD9

@VALOD đó lại là số 1 và số 3 trong danh sách của tôi. scó thể được định nghĩa là tham chiếu giá trị chỉ liên kết với các giá trị, nhưng bên trong foo, slà một giá trị.
Arne Mertz

1

Emplace_back nhận một danh sách các tham chiếu rvalue và cố gắng tạo một phần tử vùng chứa trực tiếp tại chỗ. Bạn có thể gọi emplace_back với tất cả các kiểu mà các hàm tạo phần tử vùng chứa hỗ trợ. Khi gọi emplace_back cho các tham số không phải là tham chiếu rvalue, nó 'trở lại' thành tham chiếu bình thường và ít nhất hàm tạo bản sao được gọi khi tham số và các phần tử vùng chứa có cùng kiểu. Trong trường hợp của bạn, 'myvector.emplace_back (mystring)' nên tạo một bản sao của chuỗi vì trình biên dịch không thể biết rằng tham số myvector có thể di chuyển được. Vì vậy, hãy chèn std :: di chuyển những gì mang lại cho bạn lợi ích mong muốn. Push_back sẽ hoạt động tốt như emplace_back đối với các phần tử đã được xây dựng.


2
Đó là một ... cách thú vị để mô tả các tham chiếu chuyển tiếp / phổ quát.
TC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.