Comment initialiser std :: vector à partir d'un tableau de style C?

174

Quelle est la manière la moins chère d'initialiser un std::vector partir d'un tableau de style C?

Exemple: dans la classe suivante, j'ai un vector, mais en raison de restrictions extérieures, les données seront transmises sous forme de tableau de style C:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

De toute évidence, je peux appeler w_.resize()puis boucler sur les éléments, ou appeler std::copy(). Existe-t-il de meilleures méthodes?

Franc
la source
11
Le nœud du problème est qu'il n'y a aucun moyen pour le vecteur de savoir si le même allocateur a été utilisé pour créer votre tableau de style C. En tant que tel, le vecteur doit allouer de la mémoire en utilisant son propre allocateur. Sinon, il pourrait simplement échanger le tableau sous-jacent et le remplacer par votre tableau.
Annulé

Réponses:

233

N'oubliez pas que vous pouvez traiter les pointeurs comme des itérateurs:

w_.assign(w, w + len);
Pavel Minaev
la source
3
C'est un problème de qualité de mise en œuvre. Puisque les itérateurs ont des balises qui spécifient leurs catégories, une implémentation de assignest certainement libre de les utiliser pour optimiser; au moins en VC ++, c'est exactement ce qu'il fait.
Pavel Minaev
38
La solution rapide pourrait être std :: vector <double> w_ (w, w + len);
jamk
1
Ceci copie les éléments dans un stockage nouvellement créé pour 'w_'; «w_.data» ne pointera pas vers «w». Vous devez encore désallouer «w». Il n'y a pas de transfert de propriété
Indy9000
1
Si c'est un élément après la fin, ça devrait aller (tout comme v.end()un itérateur pointant un après la fin avec un vecteur dans un cas similaire). Si vous obtenez une affirmation, alors quelque chose ne va pas ailleurs.
Pavel Minaev
1
Rapidement, cela désallouera-t-il la mémoire du tableau lorsque le vecteur sera hors de portée?
Adrian Koch
41

Vous utilisez le mot initialiser pour que l'on ne sache pas s'il s'agit d'une affectation ponctuelle ou peut se produire plusieurs fois.

Si vous avez juste besoin d'une initialisation unique, vous pouvez la placer dans le constructeur et utiliser le constructeur de vecteur à deux itérateurs:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

Sinon, utilisez assign comme suggéré précédemment:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}
Marque B
la source
1
Dans mon cas, la mission se produira à plusieurs reprises.
Frank
12

Vous pouvez `` apprendre '' automatiquement la taille du tableau:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

J'espère que vous pouvez changer l'interface en set_data comme ci-dessus. Il accepte toujours un tableau de style C comme premier argument. Il arrive juste de le prendre par référence.


Comment ça fonctionne

[Mise à jour: voir ici pour une discussion plus complète sur l'apprentissage de la taille]

Voici une solution plus générale:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

Cela fonctionne car le tableau est passé en tant que référence à un tableau. En C / C ++, vous ne pouvez pas passer un tableau en tant que fonction, au lieu de cela, il se désintégrera en un pointeur et vous perdrez la taille. Mais en C ++, vous pouvez passer une référence au tableau.

Passer un tableau par référence nécessite que les types correspondent exactement. La taille d'un tableau fait partie de son type. Cela signifie que nous pouvons utiliser le paramètre de modèle N pour connaître la taille pour nous.

Il pourrait être encore plus simple d'avoir cette fonction qui renvoie un vecteur. Avec les optimisations du compilateur appropriées en vigueur, cela devrait être plus rapide qu'il n'y paraît.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}
Aaron McDaid
la source
1
Dans le dernier échantillon, return { begin(source_array), end(source_array) };est également possible
MM
12

Eh bien, Pavel était proche, mais il existe même une solution plus simple et élégante pour initialiser un conteneur séquentiel à partir d'un tableau de style ac.

Dans ton cas:

w_ (array, std::end(array))
  • array nous obtiendra un pointeur vers le début du tableau (n'a pas attrapé son nom),
  • std :: end (array) nous obtiendra un itérateur à la fin du tableau.
Mugurel
la source
1
Que faut-il inclure / version de C ++?
Vlad
1
C'est l'un des constructeurs de std :: vector à partir d'au moins c ++ 98 ... Il s'appelle «constructeur de plage». cplusplus.com/reference/vector/vector/vector Essayez-le.
Mugurel
1
Une version plus indépendante est: w_ (std :: begin (array), std :: end (array)); (À l'avenir, vous pouvez modifier un tableau C pour un conteneur C ++).
Andrew Romanov
13
Remarquez que cela ne fonctionne que si vous avez un réel array(ce qui signifie généralement que vous copiez à partir d'un tableau global ou local (déclaré dans la fonction actuelle)). Dans le cas de l'OP, il reçoit un pointeur et une longueur, et comme ce n'est pas basé sur la longueur, ils ne peuvent pas changer pour recevoir un pointeur vers un tableau de taille ou quoi que ce soit, donc std::endne fonctionnera pas.
ShadowRanger
2
vectorne surcharge pas operator(), donc cela ne compile pas. std::endêtre appelé sur un pointeur ne sert à rien non plus (la question demande d'attribuer un vecteur à partir d'un pointeur et d'une variable de longueur séparée). Cela améliorerait votre réponse pour montrer plus de contexte sur ce que vous essayez de suggérer
MM
2

La réponse générique rapide:

std::vector<double> vec(carray,carray+carray_size); 

ou question spécifique:

std::vector<double> w_(w,w+len); 

basé sur ci - dessus : n'oubliez pas que vous pouvez traiter les pointeurs comme des itérateurs

eusoubrasileiro
la source
0

std::vector<double>::assignc'est la voie à suivre, car c'est peu de code . Mais comment ça marche, en fait? N'est-il pas redimensionné puis copié? Dans l'implémentation MS de STL, je l'utilise exactement.

J'ai peur qu'il n'y ait pas de moyen plus rapide d'implémenter (ré) initialiser votre std::vector.

Janusz Lenar
la source
et si les données devaient être partagées entre le vecteur et un tableau? Faut-il copier quoi que ce soit dans ce cas?
Vlad
est-ce une réponse ou une question? qu'est-ce que cela apporte aux réponses déjà existantes?
Jean-François Fabre
@ Jean-FrançoisFabre et qu'apporte ton commentaire? ;) vrai, c'est une mauvaise réponse donnée il y a des siècles.
Janusz Lenar