web-dev-qa-db-fra.com

C ++, copie définie sur vecteur

J'ai besoin de copier std::set à std::vector:

std::set <double> input;
input.insert(5);
input.insert(6);

std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable

Où est le problème?

127
CrocodileDundee

Vous devez utiliser un back_inserter:

std::copy(input.begin(), input.end(), std::back_inserter(output));

std::copy n'ajoute pas d'éléments au conteneur dans lequel vous insérez: il ne le peut pas; il n'y a qu'un itérateur dans le conteneur. De ce fait, si vous passez un itérateur de sortie directement à std::copy, vous devez vous assurer qu'il pointe vers une plage suffisamment large pour contenir la plage d'entrée.

std::back_inserter crée un itérateur en sortie qui appelle Push_back sur un conteneur pour chaque élément, chaque élément est donc inséré dans le conteneur. Sinon, vous auriez pu créer un nombre suffisant d'éléments dans le std::vector pour contenir la plage en cours de copie:

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

Ou, vous pouvez utiliser le std::vector constructeur de la plage:

std::vector<double> output(input.begin(), input.end()); 
201
James McNellis

Il suffit d'utiliser le constructeur pour le vecteur qui prend des itérateurs:

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

Supposons que vous souhaitiez simplement le contenu de s dans v et qu'il n'y ait rien dans v avant de copier les données sur celui-ci.

113
Jacob

voici une autre alternative en utilisant vector::assign:

theVector.assign(theSet.begin(), theSet.end());
34
TeddyC

Vous n'avez pas réservé suffisamment d'espace dans votre objet vectoriel pour contenir le contenu de votre ensemble.

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());
23
Marlon

std::copy ne peut pas être utilisé pour insérer dans un conteneur vide. Pour ce faire, vous devez utiliser un insert_iterator comme ceci:

std::set<double> input;
input.insert(5);
input.insert(6);

std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin())); 
1
Bradley Swain

Je pense que le moyen le plus efficace est de préallouer puis d’emplacer des éléments:

template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)
{
    std::vector<T> to;
    to.reserve(from.size());

    for (auto const& value : from)
        to.emplace_back(value);

    return to;
}

De cette façon, nous n'invoquerons le constructeur de copie pour chaque élément, par opposition à l'appel du constructeur par défaut, puis l'opérateur de copie pour les autres solutions répertoriées ci-dessus. Plus de précisions ci-dessous.

  1. back_inserter peut être utilisé mais il invoquera Push_back () sur le vecteur ( https://en.cppreference.com/w/cpp/iterator/back_insert_iterator ). emplace_back () est plus efficace car il évite de créer un temporaire lorsque vous utilisez Push_back () . Ce n'est pas un problème avec les types construits trivialement, mais sera une implication de performance pour les types non construits trivialement (par exemple, std :: string).

  2. Nous devons éviter de construire un vecteur avec l'argument de taille qui provoque la construction de tous les éléments par défaut (pour rien). Comme avec une solution utilisant std :: copy () , par exemple.

  3. Et, enfin, la méthode vector :: assign () ou le constructeur prenant la plage d'itérateurs ne sont pas de bonnes options car elles invoqueront std :: distance () (connaître le nombre d'éléments) sur définir itérateurs. Cela provoquera une itération supplémentaire indésirable par le biais de tous les éléments set , car il s'agit d'une structure de données d'arborescence de recherche binaire et il n'implémente pas les itérateurs à accès aléatoire.

J'espère que ça t'as aidé.

1
dshvets1