En C ++ 11, comment pourrais-je écrire une fonction (ou une méthode) qui prend un std :: array de type connu mais de taille inconnue?
// made up example
void mulArray(std::array<int, ?>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6> arr2;
std::array<int, 95> arr3;
mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);
Au cours de ma recherche, je n'ai trouvé que des suggestions d'utilisation de modèles, mais celles-ci semblent désordonnées (définitions de méthode dans l'en-tête) et excessives pour ce que j'essaie d'accomplir.
Existe-t-il un moyen simple de faire fonctionner cela, comme on le ferait avec des tableaux simples de style C?
std::vector
.std::vector
comme @TravisPessetto le recommande?Réponses:
Non. Vous ne pouvez vraiment pas faire cela à moins de faire de votre fonction un modèle de fonction (ou d'utiliser un autre type de conteneur, comme un
std::vector
, comme suggéré dans les commentaires de la question):Voici un exemple en direct .
la source
template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
La taille du
array
fait partie du type , vous ne pouvez donc pas faire tout ce que vous voulez. Il existe plusieurs alternatives.Il serait préférable de prendre une paire d'itérateurs:
Sinon, utilisez
vector
au lieu de array, qui vous permet de stocker la taille au moment de l'exécution plutôt que dans le cadre de son type:la source
J'ai essayé ci-dessous et cela a fonctionné pour moi.
PRODUCTION :
1 2 3 4 5 6 7
2 4 6 8 10 12
1 1 1 1 1 1 1 1 1
3 6 9 12 15 18 21
10 20 30 40 50 60
2 2 2 2 2 2 2 2 2
la source
template
.auto foo(auto bar) { return bar * 2; }
n'est pas actuellement C ++ valide même s'il compile dans GCC7 avec le jeu d'indicateurs C ++ 17. D'après la lecture ici , les paramètres de fonction déclarés comme auto font partie des concepts TS qui devraient éventuellement faire partie de C ++ 20.ÉDITER
C ++ 20 inclut provisoirement
std::span
https://en.cppreference.com/w/cpp/container/span
Réponse originale
Ce que vous voulez, c'est quelque chose comme
gsl::span
, qui est disponible dans la bibliothèque de prise en charge des lignes directrices décrite dans les lignes directrices de base C ++:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views
Vous pouvez trouver une implémentation open-source de la GSL uniquement en-tête ici:
https://github.com/Microsoft/GSL
Avec
gsl::span
, vous pouvez faire ceci:Le problème avec
std::array
est que sa taille fait partie de son type, vous devez donc utiliser un modèle pour implémenter une fonction qui prend unestd::array
taille arbitraire.gsl::span
d'autre part, stocke sa taille en tant qu'informations d'exécution. Cela vous permet d'utiliser une fonction non-modèle pour accepter un tableau de taille arbitraire. Il acceptera également d'autres conteneurs contigus:Assez cool, hein?
la source
Absolument, il existe un moyen simple en C ++ 11 d'écrire une fonction qui prend un std :: array de type connu, mais de taille inconnue.
Si nous ne pouvons pas transmettre la taille du tableau à la fonction, alors à la place, nous pouvons transmettre l'adresse mémoire de l'endroit où le tableau commence avec une deuxième adresse de l'endroit où le tableau se termine. Plus tard, à l'intérieur de la fonction, nous pouvons utiliser ces 2 adresses mémoire pour calculer la taille du tableau!
Sortie à la console: 10, 20, 2, 4, 8
la source
Cela peut être fait, mais il faut quelques étapes pour le faire proprement. Tout d'abord, écrivez un
template class
qui représente une plage de valeurs contiguës. Ensuite, transférez unetemplate
version qui connaît la taillearray
de laImpl
version qui prend cette plage contiguë.Enfin, implémentez la
contig_range
version. Notez que celafor( int& x: range )
fonctionne pourcontig_range
, car j'ai implémentébegin()
etend()
et les pointeurs sont des itérateurs.(non testé, mais la conception devrait fonctionner).
Ensuite, dans votre
.cpp
dossier:Cela a l'inconvénient que le code qui boucle sur le contenu du tableau ne sait pas (au moment de la compilation) la taille du tableau, ce qui pourrait coûter l'optimisation. Il présente l'avantage que l'implémentation ne doit pas nécessairement figurer dans l'en-tête.
Faites attention à ne pas construire explicitement a
contig_range
, comme si vous le transmettez aset
cela supposera que lesset
données sont contiguës, ce qui est faux, et fera un comportement indéfini partout. Les deux seulsstd
conteneurs sur lesquels cela est garanti de fonctionner sontvector
etarray
(et les tableaux de style C, en l'occurrence!).deque
bien que l'accès aléatoire ne soit pas contigu (dangereusement, il est contigu par petits morceaux!),list
n'est même pas proche, et les conteneurs associatifs (ordonnés et non ordonnés) sont également non contigus.Donc, les trois constructeurs que j'ai implémentés où
std::array
,std::vector
et les tableaux de style C, qui couvrent essentiellement les bases.La mise en œuvre
[]
est également facile, et entrefor()
et[]
c'est ce que vous recherchezarray
, n'est-ce pas?la source
template
fonction très courte avec pratiquement aucun détail d'implémentation. LaImpl
fonction n'est pas unetemplate
fonction, vous pouvez donc cacher l'implémentation dans le.cpp
fichier de votre choix. C'est une sorte d'effacement de type vraiment grossier, où j'extrait la capacité d'itérer sur des conteneurs contigus dans une classe plus simple, puis de la passer à travers ... (bien quemultArrayImpl
prenne atemplate
comme argument, ce n'est pas untemplate
lui - même).&*
déréférence l'itérateur (qui peut ne pas être un pointeur), puis fait un pointeur vers l'adresse. Pour les données de mémoire contiguës, le pointeur versbegin
et le pointeur vers un après-leend
sont également des itérateurs à accès aléatoire, et ils sont du même type pour chaque plage contiguë sur un typeT
.