Tôi có thể liệt kê khởi tạo std :: vector với chuyển tiếp hoàn hảo các phần tử không?


14

Tôi nhận thấy rằng danh sách tổng hợp initalization của std :: vector thực hiện khởi tạo bản sao khi di chuyển được áp dụng nhiều hơn. Đồng thời, nhiều emplace_backs làm những gì tôi muốn.

Tôi chỉ có thể đưa ra giải pháp không hoàn hảo này bằng cách viết một hàm mẫu init_emplace_vector. Tuy nhiên, nó chỉ tối ưu cho các hàm tạo giá trị đơn không rõ ràng .

template <typename T, typename... Args>
std::vector<T> init_emplace_vector(Args&&... args)
{
  std::vector<T> vec;
  vec.reserve(sizeof...(Args));  // by suggestion from user: eerorika
  (vec.emplace_back(std::forward<Args>(args)), ...);  // C++17
  return vec;
}

Câu hỏi

Tôi có thực sự cần sử dụng emplace_back để khởi tạo std :: vector hiệu quả nhất có thể không?

// an integer passed to large is actually the size of the resource
std::vector<large> v_init {
  1000,  // instance of class "large" is copied
  1001,  // copied
  1002,  // copied
};

std::vector<large> v_emplaced;
v_emplaced.emplace_back(1000);  // moved
v_emplaced.emplace_back(1001);  // moved
v_emplaced.emplace_back(1002);  // moved

std::vector<large> v_init_emplace = init_emplace_vector<large>(
  1000,   // moved
  1001,   // moved
  1002    // moved
);

Đầu ra

Lớp largetạo thông tin về các bản sao / di chuyển (triển khai bên dưới) và vì vậy đầu ra của chương trình của tôi là:

- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move

Thực hiện lớp lớn

Việc triển khai của tôi largechỉ đơn giản là một loại có thể sao chép / di chuyển được, nắm giữ một nguồn tài nguyên lớn cảnh báo về việc sao chép / di chuyển.

struct large
{
  large(std::size_t size) : size(size), data(new int[size]) {}

  large(const large& rhs) : size(rhs.size), data(new int[rhs.size])
  {
    std::copy(rhs.data, rhs.data + rhs.size, data);
    std::puts("large copy");
  }

  large(large&& rhs) noexcept : size(rhs.size), data(rhs.data)
  {
    rhs.size = 0;
    rhs.data = nullptr;
    std::puts("large move");
  }

  large& operator=(large rhs) noexcept
  {
    std::swap(*this, rhs);
    return *this;
  }

  ~large() { delete[] data; }

  int* data;
  std::size_t size;
};

Biên tập

Bằng cách sử dụng dự trữ, không có bản sao hoặc di chuyển. Chỉ có large::large(std::size_t)constructor được gọi. Nơi thật sự.


1
Đó không phải là khởi tạo tổng hợp, bạn đang gọi hàm tạo có một std::initializer_list.
siêu

Các nhà xây dựng của std :: vector là một IMHO lộn xộn khó hiểu, và nếu tôi là bạn, tôi thực sự sẽ tránh bị xâm nhập nếu tôi hoàn toàn không phải làm thế. Ngoài ra, nếu bạn biết trước nội dung của vectơ (có vẻ như đó là trường hợp) - hãy xem xét một std::array.
einpoklum

1
Việc operator=phá hủy nguồn này khá bất thường và có thể gây ra sự cố không mong muốn.
alain

1
init_emplace_vectorcó thể được cải thiện vớivec.reserve(sizeof...(Args))
Indiana Kernick

1
@alain operator=không phá hủy nguồn. Đó là thành ngữ hoán đổi bản sao.
nối lại

Câu trả lời:


11

Tôi có thể tổng hợp khởi tạo std :: vector ...

Số std::vectorkhông phải là tổng hợp, vì vậy nó không thể được tổng hợp khởi tạo.

Bạn có thể có nghĩa là khởi tạo danh sách thay vào đó, trong trường hợp này:

Tôi có thể [liệt kê danh sách] std :: vector với chuyển tiếp hoàn hảo các phần tử không?

Không. Khởi tạo danh sách sử dụng hàm tạo std::initializer_liststd::initializer_listsao chép các đối số của nó.

init_emplace_vectorDường như của bạn là một giải pháp tốt, mặc dù nó có thể được cải thiện bằng cách đặt bộ nhớ trước khi đặt các phần tử.


1
Cảm ơn vì đã sửa tôi. Đã chỉnh sửa. Điểm tốt với dự trữ.
nối lại
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.