J'ai remarqué que globale liste initalization de std :: vecteur effectue l' initialisation de copie lors de déplacement est plus applicable. En même temps, plusieurs emplace_backs font ce que je veux.
Je ne pouvais que trouver cette solution imparfaite d'écrire une fonction de modèle init_emplace_vector
. Cependant, il n'est optimal que pour les constructeurs à valeur unique non explicites .
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;
}
Question
Ai-je vraiment besoin d'utiliser emplace_back pour initialiser std :: vector aussi efficacement que possible?
// 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
);
Production
La classe large
produit des informations sur les copies / mouvements (implémentation ci-dessous) et donc la sortie de mon programme est:
- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move
Implémentation de grande classe
Mon implémentation de large
est simplement un type copiable / mobile contenant une grande ressource qui avertit lors de la copie / déplacement.
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;
};
Éditer
En utilisant reserve, il n'y a pas de copie ni de déplacement. Seul le large::large(std::size_t)
constructeur est invoqué. Véritable lieu de travail.
la source
std::initializer_list
.std::array
.operator=
élément qui détruit la source est assez inhabituel et peut provoquer des problèmes inattendus.init_emplace_vector
pourrait être amélioré avecvec.reserve(sizeof...(Args))
operator=
ne détruit pas la source. C'est l'idiome d'échange de copie.Réponses:
Non
std::vector
n'est pas un agrégat, il ne peut donc pas être initialisé par agrégat.Vous pouvez plutôt signifier l'initialisation de la liste, auquel cas:
Non. L'initialisation de liste utilise le
std::initializer_list
constructeur etstd::initializer_list
copie ses arguments.Votre
init_emplace_vector
semble être une solution décente, bien qu'elle puisse être améliorée en réservant la mémoire avant de placer les éléments.la source