Posso inizializzare la lista std :: vector con un perfetto inoltro degli elementi?


14

Ho notato che l' inizializzazione dell'elenco aggregato di std :: vector esegue l' inizializzazione della copia quando lo spostamento è più applicabile. Allo stesso tempo, più emplace_backs fanno quello che voglio.

Potrei solo trovare questa soluzione imperfetta di scrivere una funzione modello init_emplace_vector. Tuttavia, è ottimale solo per costruttori non espliciti a valore singolo .

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

Domanda

Devo davvero usare emplace_back per inizializzare std :: vector nel modo più efficiente possibile?

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

Produzione

La classe largeproduce informazioni su copie / mosse (implementazione di seguito) e quindi l'output del mio programma è:

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

Implementazione di grande classe

La mia implementazione di largeè semplicemente un tipo copiabile / mobile che contiene una grande risorsa che avvisa su copia / sposta.

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

modificare

Usando la riserva, non c'è copia o spostamento. large::large(std::size_t)Viene invocato solo il costruttore. Vero posto.


1
Non è l'inizializzazione aggregata, stai chiamando il costruttore che accetta un std::initializer_list.
Super

I costruttori di std :: vector sono un disordine confuso, e se fossi in te eviterei davvero di entrarci se non dovessi assolutamente farlo. Inoltre, se conosci in anticipo i contenuti dei tuoi vettori (il che sembra che potrebbe essere il caso), considera un std::array.
einpoklum,

1
Ciò operator=che distrugge la fonte è abbastanza insolito e potrebbe causare problemi imprevisti.
alain

1
init_emplace_vectorpotrebbe essere migliorato convec.reserve(sizeof...(Args))
Indiana Kernick il

1
@alain operator=non distrugge la fonte. È il linguaggio della copia-scambio.
Riconnetti il

Risposte:


11

Posso aggregare-inizializzare std :: vector ...

No. std::vectornon è un aggregato, quindi non può essere inizializzato con aggregato.

Puoi invece indicare l'inizializzazione dell'elenco, nel qual caso:

Posso [list-inizializzare] std :: vector con un perfetto inoltro degli elementi?

No. L'inizializzazione dell'elenco utilizza il std::initializer_listcostruttore e std::initializer_listcopia i suoi argomenti.

La tua init_emplace_vectorsembra essere una soluzione decente, sebbene possa essere migliorata riservando la memoria prima di inserire gli elementi.


1
Grazie per avermi corretto. Editted. Buon punto con riserva.
Riconnetti il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.