J'utilise une bibliothèque externe qui à un moment donné me donne un pointeur brut vers un tableau d'entiers et une taille.
Maintenant, j'aimerais utiliser std::vector
pour accéder et modifier ces valeurs en place, plutôt que d'y accéder avec des pointeurs bruts.
Voici un exemple explicatif qui explique le point:
size_t size = 0;
int * data = get_data_from_library(size); // raw data from library {5,3,2,1,4}, size gets filled in
std::vector<int> v = ????; // pseudo vector to be used to access the raw data
std::sort(v.begin(), v.end()); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
Production attendue:
1
2
3
4
5
La raison en est que je dois appliquer des algorithmes à partir de <algorithm>
(tri, échange d'éléments, etc.) sur ces données.
D'autre part changer la taille de ce vecteur ne serait jamais changé, donc push_back
, erase
, insert
ne sont pas tenus de travailler sur ce vecteur.
Je pourrais construire un vecteur basé sur les données de la bibliothèque, utiliser modifier ce vecteur et copier les données dans la bibliothèque, mais ce serait deux copies complètes que j'aimerais éviter car l'ensemble de données pourrait être très volumineux.
std::vector_view
, n'est-ce pas?std::vector
marche.sort(arrayPointer, arrayPointer + elementCount);
.Réponses:
Le problème est qu'il
std::vector
faut faire une copie des éléments du tableau avec lequel vous l'initialisez car il a la propriété des objets qu'il contient.Pour éviter cela, vous pouvez utiliser un objet tranche pour un tableau (c'est-à-dire similaire à ce qui
std::string_view
est destinéstd::string
). Vous pouvez écrire votre proprearray_view
implémentation de modèle de classe dont les instances sont construites en prenant un pointeur brut vers le premier élément d'un tableau et la longueur du tableau:array_view
ne stocke pas de tableau; il contient juste un pointeur sur le début du tableau et la longueur de ce tableau. Par conséquent, lesarray_view
objets sont bon marché à construire et à copier.Depuis
array_view
fournit lesbegin()
etend()
fonctions membres, vous pouvez utiliser les algorithmes de la bibliothèque standard (par exemple,std::sort
,std::find
,std::lower_bound
, etc.) sur elle:Production:
Utilisez
std::span
(ougsl::span
) à la placeL'implémentation ci-dessus expose le concept derrière les objets tranche . Cependant, depuis C ++ 20, vous pouvez utiliser directement à la
std::span
place. Dans tous les cas, vous pouvez utilisergsl::span
depuis C ++ 14.la source
C ++ 20's
std::span
Si vous pouvez utiliser C ++ 20, vous pouvez utiliser
std::span
une paire pointeur-longueur qui donne à l'utilisateur une vue dans une séquence d'éléments contigus. Il s'agit d'une sorte destd::string_view
, et bien que les deuxstd::span
et nestd::string_view
soient pas des vues propriétaires,std::string_view
c'est une vue en lecture seule.De la documentation:
Donc, ce qui suit fonctionnerait:
Découvrez-le en direct
Comme il
std::span
s'agit essentiellement d'une paire pointeur-longueur, vous pouvez également utiliser de la manière suivante:Remarque: tous les compilateurs ne prennent pas en charge
std::span
. Vérifiez le support du compilateur ici .MISE À JOUR
Si vous n'êtes pas en mesure d'utiliser C ++ 20, vous pouvez utiliser
gsl::span
qui est essentiellement la version de base de la norme C ++std::span
.Solution C ++ 11
Si vous êtes limité au standard C ++ 11, vous pouvez essayer d'implémenter votre propre
span
classe simple :Découvrez la version C ++ 11 en direct
la source
gsl::span
pour C ++ 14 et supérieur si votre compilateur ne met pas en œuvrestd::span
Puisque la bibliothèque d'algorithmes fonctionne avec les itérateurs, vous pouvez conserver le tableau.
Pour les pointeurs et la longueur de tableau connue
Ici, vous pouvez utiliser des pointeurs bruts comme itérateurs. Ils supportent toutes les opérations supportées par un itérateur (incrément, comparaison pour égalité, valeur de, etc ...):
data
pointe vers le premier membre du tableau comme un itérateur renvoyé parbegin()
etdata + size
pointe vers l'élément après le dernier élément du tableau comme un itérateur renvoyé parend()
.Pour les tableaux
Ici, vous pouvez utiliser
std::begin()
etstd::end()
Mais gardez à l'esprit que cela ne fonctionne que s'il
data
ne se désintègre pas en un pointeur, car les informations de longueur manquent.la source
Vous pouvez obtenir des itérateurs sur des tableaux bruts et les utiliser dans des algorithmes:
Si vous travaillez avec des pointeurs bruts (ptr + taille), vous pouvez utiliser la technique suivante:
UPD: Cependant, l'exemple ci-dessus est de mauvaise conception. La bibliothèque nous renvoie un pointeur brut et nous ne savons pas où le tampon sous-jacent est alloué et qui est censé le libérer.
Habituellement, l'appelant fournit un tampon pour que la fonction remplisse les données. Dans ce cas, nous pouvons préallouer le vecteur et utiliser son tampon sous-jacent:
Lorsque vous utilisez C ++ 11 ou supérieur, nous pouvons même faire get_data_from_library () pour retourner un vecteur. Grâce aux opérations de déplacement, il n'y aura pas de copie mémoire.
la source
auto begin = data;
auto end = data + size;
get_data_from_library()
sont allouées? Peut-être que nous ne sommes pas censés le changer du tout. Si nous devons passer un tampon à la bibliothèque, alors nous pouvons allouer le vecteur et passerv.data()
Vous ne pouvez pas le faire avec un
std::vector
sans faire de copie.std::vector
possède le pointeur qu'il a sous le capot et alloue de l'espace via l'allocateur fourni.Si vous avez accès à un compilateur qui prend en charge C ++ 20, vous pouvez utiliser std :: span qui a été construit exactement à cette fin. Il encapsule un pointeur et une taille dans un "conteneur" qui a l'interface de conteneur C ++.
Sinon, vous pouvez utiliser gsl :: span qui est la base de la version standard.
Si vous ne souhaitez pas importer une autre bibliothèque, vous pouvez l'implémenter trivialement vous-même en fonction de toutes les fonctionnalités que vous souhaitez avoir.
la source
Vous ne pouvez pas. Ce n'est pas pour ça
std::vector
.std::vector
gère son propre tampon, qui est toujours acquis à partir d'un allocateur. Il ne prend jamais possession d'un autre tampon (sauf d'un autre vecteur du même type).D'un autre côté, vous n'avez pas non plus besoin de le faire parce que ...
Ces algorithmes fonctionnent sur des itérateurs. Un pointeur est un itérateur d'un tableau. Vous n'avez pas besoin d'un vecteur:
Contrairement aux modèles de fonctions dans
<algorithm>
, certains outils tels que range-for,std::begin
/std::end
et C ++ 20 ne fonctionnent pas avec seulement une paire d'itérateurs, alors qu'ils fonctionnent avec des conteneurs tels que des vecteurs. Il est possible de créer une classe wrapper pour itérateur + taille qui se comporte comme une plage et fonctionne avec ces outils. C ++ 20 introduiront une enveloppe dans la bibliothèque standard:std::span
.la source
En plus de l'autre bonne suggestion de
std::span
venir en c ++ 20 etgsl:span
, y compris votre proprespan
classe (légère) jusque-là est déjà assez facile (n'hésitez pas à copier):Nous tenons à souligner également la bibliothèque de gamme boost boost portée si vous êtes intéressé par le concept de gamme plus générique: https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference /utilities/iterator_range.html .
Les concepts de plage arriveront également en c ++ 20
la source
using value_type = std::remove_cv_t<T>;
sert?span(T* first_, size_t length) : first(first), length(length) {};
. J'ai modifié votre réponse.using value_type = std::remove_cv_t<T>;
est principalement nécessaire s'il est utilisé avec la programmation de modèles (pour obtenir le type_valeur d'une «plage»). Si vous souhaitez simplement utiliser les itérateurs, vous pouvez ignorer / supprimer cela.En fait, vous pourriez presque l'utiliser
std::vector
pour cela, en abusant de la fonctionnalité d'allocateur personnalisé pour renvoyer un pointeur vers la mémoire que vous souhaitez afficher. Cela ne serait pas garanti par la norme de fonctionner (remplissage, alignement, initialisation des valeurs renvoyées; vous auriez à vous soucier de l'attribution de la taille initiale, et pour les non primitifs, vous auriez également besoin de pirater vos constructeurs ), mais en pratique, je m'attendrais à ce qu'il donne suffisamment de réglages.Ne fais jamais ça. C'est moche, surprenant, hacky et inutile. Les algorithmes de la bibliothèque standard sont déjà conçus pour fonctionner aussi bien avec des tableaux bruts qu'avec des vecteurs. Voir les autres réponses pour plus de détails.
la source
vector
constructeurs qui prennent une référence d'allocateur personnalisée comme argument de constructeur (pas seulement un paramètre de modèle). Je suppose que vous auriez besoin d'un objet allocateur contenant la valeur du pointeur d'exécution, pas en tant que paramètre de modèle, sinon il ne pourrait fonctionner que pour les adresses constexpr. Vous devez faire attention à ne pas laisser d'vector
objets de construction par défaut sur.resize()
et écraser les données existantes; l'inadéquation entre un conteneur propriétaire comme un vecteur par rapport à une plage non propriétaire est énorme si vous commencez à utiliser .push_back, etc.construct
méthode qui serait requise ... Je ne peux pas penser quels cas d'utilisation non hacky exigeraient cela par rapport à placement-new.resize()
avant de passer une référence à quelque chose qui veut l'utiliser comme une sortie pure (par exemple un appel système en lecture). Dans la pratique, les compilateurs n'optimisent souvent pas cet ensemble de mems ou autre. Ou si vous aviez un allocateur qui utilise calloc pour obtenir de la mémoire pré-mise à zéro, vous pourriez également éviter de le salir comme stupide lestd::vector<int>
fait par défaut lors de la construction d'objets par défaut qui ont un modèle de bits à zéro. Voir les notes dans en.cppreference.com/w/cpp/container/vector/vectorComme d'autres l'ont souligné,
std::vector
doit posséder la mémoire sous-jacente (à moins de jouer avec un allocateur personnalisé) donc ne peut pas être utilisé.D'autres ont également recommandé l'étendue de c ++ 20, mais cela nécessite évidemment c ++ 20.
Je recommanderais le span-lite span. Pour citer son sous-titre:
Il fournit une vue non propriétaire et modifiable (comme dans vous pouvez muter les éléments et leur ordre mais pas les insérer) et comme le dit la citation n'a pas de dépendances et fonctionne sur la plupart des compilateurs.
Votre exemple:
Impressions
Cela a également l'avantage supplémentaire si un jour vous passez au c ++ 20, vous devriez simplement être en mesure de le remplacer
nonstd::span
parstd::span
.la source
Vous pouvez utiliser un
std::reference_wrapper
disponible depuis C ++ 11:la source
std::copy(std::begin(src_table), std::end(src_table), std::back_inserter(dest_vector));
remplit définitivement ledest_vector
avec les valeurs tirées desrc_table
(IOW les données sont copiéesdest_vector
), donc je n'ai pas reçu votre commentaire. Pourriez-vous expliquer?