Bisakah saya daftar-menginisialisasi std :: vektor dengan penerusan yang sempurna dari elemen?

14

Saya perhatikan bahwa daftar agregasi inisialisasi std :: vector melakukan inisialisasi salin ketika bergerak lebih berlaku. Pada saat yang sama, banyak emplace_backs melakukan apa yang saya inginkan.

Saya hanya bisa menghasilkan solusi yang tidak sempurna untuk menulis fungsi templat ini init_emplace_vector. Ini hanya optimal untuk konstruktor nilai tunggal yang tidak eksplisit .

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;
}

Pertanyaan

Apakah saya benar-benar perlu menggunakan emplace_back untuk menginisialisasi std :: vector seefisien mungkin?

// 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
);

Keluaran

Kelas largemenghasilkan informasi tentang salinan / gerakan (implementasi di bawah) dan output dari program saya adalah:

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

Implementasi kelas besar

Implementasi saya largehanyalah tipe yang dapat disalin / bergerak yang menyimpan sumber daya besar yang memperingatkan tentang penyalinan / pemindahan.

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;
};

Edit

Dengan menggunakan cadangan, tidak ada salinan atau pemindahan. Hanya large::large(std::size_t)konstruktor yang dipanggil. Emplace sejati.

terhubung kembali
sumber
1
Ini bukan inisialisasi agregat, Anda memanggil konstruktor yang mengambil a std::initializer_list.
super
std :: vector's constructor adalah kekacauan IMHO yang membingungkan, dan jika saya jadi Anda, saya akan benar-benar menghindari masuk ke dalamnya jika saya tidak benar-benar harus. Plus, jika Anda tahu konten vektor Anda sebelumnya (yang sepertinya memang demikian) - pertimbangkan sebuah std::array.
einpoklum
1
Yang operator=menghancurkan sumbernya sangat tidak biasa dan dapat menyebabkan masalah yang tidak terduga.
alain
1
init_emplace_vectordapat ditingkatkan denganvec.reserve(sizeof...(Args))
Indiana Kernick
1
@ alain operator=tidak merusak sumbernya. Ini idiom copy-swap.
menghubungkan kembali

Jawaban:

11

Bisakah saya menggabungkan-menginisialisasi std :: vector ...

Tidak std::vectorbukanlah agregat, sehingga tidak dapat agregat-dijalankan.

Anda dapat berarti inisialisasi daftar sebagai gantinya, dalam hal ini:

Bisakah saya [daftar-inisialisasi] std :: vektor dengan penerusan sempurna elemen?

Tidak. Inisialisasi daftar menggunakan std::initializer_listkonstruktor dan std::initializer_listmenyalin argumennya.

init_emplace_vectorTampaknya Anda merupakan solusi yang layak, meskipun dapat ditingkatkan dengan menyimpan memori sebelum menggunakan elemen-elemen tersebut.

eerorika
sumber
1
Terima kasih telah mengoreksi saya. Diedit. Poin bagus dengan cadangan.
menghubungkan kembali