web-dev-qa-db-fra.com

Vecteur STL: déplacement de tous les éléments d'un vecteur

J'ai deux vecteurs STL A et B et j'aimerais effacer tous les éléments de A et déplacer tous les éléments de B à A, puis effacer B. En termes simples, je veux faire ceci:

std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();

Comme B peut être assez long, il faut k*O(N) pour effectuer cette opération, où k est une constante et N est max(size_of(A), size_of(B)). Je me demandais s'il pourrait y avoir un moyen plus efficace de le faire. Une chose à laquelle je pourrais penser est de définir A et B en tant que pointeurs, puis de les copier en temps constant et de vider B.

40
aminfar

En utilisant C++ 11, c'est aussi simple que:

A = std::move(B);

Maintenant, A contient les éléments précédemment détenus par B, et B est maintenant vide. Cela évite la copie: la représentation interne est simplement déplacée de B à A, il s'agit donc d'une solution O(1).

En ce qui concerne C++ 03, comme le prétendent les préteurs, vous pouvez échanger les vecteurs. Il existe une spécialisation de la fonction std::swap, qui prend std::vectors comme argument. Cela échange efficacement la représentation interne, vous évitant ainsi de créer des copies des éléments qu’ils détiennent. Cette fonction fonctionne également dans la complexité O(1).

65
mfontanini

Si vous avez un compilateur C++ 11, vous pouvez déplacer B dans A.

A = std::move(B);

Si vous travaillez avec un ancien compilateur, swap les deux

A.swap(B);

Dans les deux cas, la seule opération O(N) effacera le contenu de A. Dans le premier cas, l'effacement sera effectué pendant la tâche elle-même, tandis que dans le second, cela se produira lorsque B sort du cadre (puisque le contenu a été échangé).

12
Praetorian

J'ai deux vecteurs STL A et B et j'aimerais effacer tous les éléments de A, déplacer tous les éléments de B en A, puis effacer B.

Cela peut être fait avec une combinaison de swap. Premier échange A et B pour la première moitié. Ensuite, swap un std::vector<> vide avec B ou appelez clear(). La différence est que clear() ne libère pas la mémoire, mais détruit uniquement les objets:

std::vector<int> a, b; // initialize them somehow
swap(a,b);

// clear b without releasing the memory:
std::size_t capacity = b.capacity();
b.clear();
assert(b.capacity()==capacity);

// or release the memory
std::vector<int>().swap(b);
assert(b.capacity()==0);

il suffit d’appeler clear clear on vector o(1) car clear ne fera rien, Si vous voulez vraiment effacer B après l’affecter à A, vous pouvez procéder comme suit:

A.swap(B);
{
    std::Vector<..> C;
    c.swap(B);
}
3
mogulkahn

La fonction swap fait cela.

#include <iostream>
#include <iterator>
#include <vector>

int main(int argc, char* argv)
{
  std::vector<int> A;
  std::vector<int> B;

  for (int i = 0; i < 10; ++i)
  {
     B.Push_back(i);
  }

  std::cout << "Before swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";

  A.swap(B);
  B.clear();

  std::cout << "After swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";
}

Le résultat

Before swap
A:
B:0 1 2 3 4 5 6 7 8 9 
After swap
A:0 1 2 3 4 5 6 7 8 9 
B:
2
Jason

Si vous ne pouvez pas std :: move ou std :: swap les vecteurs (par exemple, parce que A et B sont liés mais de types différents, peut-être ne différant que par const), vous pouvez faire:

std::vector<MyClass>       A;
std::vector<const MyClass> B;
// ...
for( auto& a : A )
{
    B.emplace_back( std::move( a ) );
}

Notez que cela laisse A avec le même nombre d’éléments, mais ils sont tous dans un état indéterminé (c’est-à-dire qu’ils peuvent être attribués ou détruits, mais pas lus).

1
metal

std :: move fonctionne bien. Voici l'exemple de code pour le même

    vector<int> v1 = {1,2,3,10,20,30,100,200,300,999};
    vector<int> v2;

    cout << "Size of v1 before move = " << v1.size() << endl;
    cout << "Capacity of v1 before move = " << v1.capacity() << endl;

    v2 = std::move(v1);

    cout << "Size of v2 after move = " << v2.size() << endl;
    cout << "Capacity of v2 after move = " << v2.capacity() << endl;

    cout << "Size of v1 after move = " << v1.size() << endl;
    cout << "Capacity of v1 after move = " << v1.capacity() << endl;

-----------Output-------------------------
Size of v1 before move = 10
Capacity of v1 before move = 10
Size of v2 after move = 10
Capacity of v2 after move = 10
Size of v1 after move = 0
Capacity of v1 after move = 0
1
Sameer Ahuja

Il me manque un représentant pour commenter, mais je tiens à mentionner que: https://en.cppreference.com/w/cpp/container/vector/operator%3D void.pointer a raison. En particulier...

2) Opérateur d’affectation de déplacement. Remplace le contenu par ceux d’autre en utilisant la sémantique de déplacement (c’est-à-dire que les données d’autre sont déplacées d’autre dans ce conteneur). l'autre est dans un état valide mais non spécifié par la suite.

Ainsi, la réponse de Praetorian est fausse par norme. Cependant, pour MSVC au moins, cela suffit, car l’implémentation efface quand même la liste (probablement pour la plupart des personnes).

Quelque chose d’intéressant, puisque nous déclarons un constructeur de déplacement, aucun opérateur d’affectation de déplacement implicite ne sera déclaré. Ainsi, nous "savons" que std :: vector doit déclarer un opérateur d’assignation de mouvement.

0
redfeatherplusplus