79 votes

comment initialiser 'const std::vector<T>' comme un tableau c

Existe-t-il une manière élégante de créer et d'initialiser une const std::vector<const T> comme const T a[] = { ... } à un nombre fixe (et réduit) de valeurs ?
J'ai besoin d'appeler fréquemment une fonction qui attend un vector<T> mais ces valeurs ne changeront jamais dans mon cas.

En principe, je pensais à quelque chose comme

namespace {
  const std::vector<const T> v(??);
}

puisque v ne sera pas utilisé en dehors de cette unité de compilation.

61voto

Ferruccio Points 51508

Vous devez soit attendre le C++0x, soit utiliser quelque chose comme Boost.Assign pour le faire.

Par exemple :

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;

pour C++11 :

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };

1 votes

Le terme "devoir" est peut-être un peu fort ; l'approche en deux étapes peut également fonctionner. L'approche en deux étapes peut également fonctionner.

0 votes

Je pense que vous vouliez dire : #include <boost/assign/std/vector.hpp>

1 votes

@Jason : boost.assign définit des surcharges modélisées de operator+= et operator, sur vector<T>.

39voto

Steve Jessop Points 166970

Si vous vous demandez comment initialiser un vecteur constant pour qu'il ait un contenu intéressant, la réponse est probablement d'utiliser le constructeur copy. D'abord, vous remplissez laborieusement un vecteur, puis vous créez votre nouveau vecteur constant à partir de celui-ci. Vous pouvez également utiliser le modèle de constructeur vector<InputIterator>(InputIterator, InputIterator) pour initialiser à partir d'un autre type de conteneur ou d'un tableau. S'il s'agit d'un tableau, celui-ci peut avoir été défini avec une liste d'initialisation.

On peut espérer que quelque chose comme ça se rapproche de ce que vous voulez :

const T ra[3] = {t1, t2, t3};
const vector<const T> v(ra, ra+3);

Si vous demandez comment passer un vecteur constant dans une fonction qui prend un vecteur, la réponse est l'une ou l'autre :

  • vous ne pouvez pas, car la fonction pourrait modifier le vecteur et votre objet/référence est const. Faites une copie non-const de l'original, et passez-la.

ou

  • utiliser const_cast pour supprimer la constance afin de le passer dans une fonction qui prend un vecteur non-const mais dont vous savez pertinemment qu'elle ne modifiera pas le vecteur.

Ce dernier point fait partie de ces choses qui, à juste titre, amèneront tous ceux qui le verront à faire des commentaires sur les lunettes et le fait qu'elles ne font rien. C'est exactement ce à quoi sert const_cast, mais il y a un argument assez fort qui dit que si vous avez besoin de const_cast, vous avez déjà perdu.

Faire ces deux choses (créer un vecteur constant à partir d'un vecteur non constant avec le constructeur de copie, et ensuite enlever la constance) est définitivement incorrect - vous auriez dû simplement utiliser un vecteur non constant. Choisissez donc au maximum l'une de ces choses à faire...

Edition : je viens de remarquer que vous parlez d'une différence entre vector<T> et const vector<const T>. Malheureusement, dans la STL, vector<const T> et vector<T> sont des types complètement indépendants, et le seul moyen de les convertir est de les copier. Il s'agit d'une différence entre les vecteurs et les tableaux - un T** peut être converti silencieusement et en toute sécurité en const T *const *].

0 votes

Si je ne veux pas copier t1, t2, t3, comment puis-je le faire au mieux ? C'est la seule/meilleure solution que j'ai trouvée jusqu'à présent : <code>const int *p = &ra[0] ; vector<const int *> v ; for(int i = 0 ; i < taille ; ++i, ++p) v.push_back(p);</code>

0 votes

@AudioDroid : oui, il n'y a aucun moyen de mettre quelque chose dans un vecteur sans le copier (ou le déplacer, en C++11), donc le plus proche que vous obtiendrez est un vecteur de pointeurs (ou pointeurs intelligents). Mais si vous avez déjà vos trois objets dans un tableau, alors normalement il n'y a pas de raison de créer un vecteur de pointeurs vers eux : utilisez simplement le tableau pour ce pour quoi vous utiliseriez le vecteur.

15voto

Shadow2531 Points 6726

Une méthode simple et efficace (similaire à la méthode list_of() de Boost)

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(const T& t) {
        (*this)(t);
    }
    vlist_of& operator()(const T& t) {
        this->push_back(t);
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
}

Maintenant, C++11 a des listes d'initialisateurs, donc vous n'avez pas besoin de le faire de cette façon ou même d'utiliser Boost. Mais, à titre d'exemple, vous pouvez faire ce qui précède en C++11 de manière plus efficace comme ceci :

#include <iostream>
#include <vector>
#include <utility>
#include <ostream>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(T&& t) {
        (*this)(move(t));
    }
    vlist_of& operator()(T&& t) {
        this->push_back(move(t));
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    for (const auto& i: v) {
        cout << i << endl;
    }
}

Mais ce n'est toujours pas aussi efficace que l'utilisation d'une liste d'initialisation C++11 car il n'y a pas d'operator=(vlist_of&&) défini pour le vecteur.

La méthode de tjohns20 modifiée comme suit pourrait être une meilleure vlist_of c++11 :

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

template <typename T>
class vlist_of {
    public:
        vlist_of(T&& r) {
            (*this)(move(r));
        }
        vlist_of& operator()(T&& r) {
            v.push_back(move(r));
            return *this;
        }
        vector<T>&& operator()() {
            return move(v);
        }
    private:
        vector<T> v;

};

int main() {
    const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
    for (const auto& i : v) {
        cout << i << endl;
    }

}

0 votes

Méchant, mais utile pour les tests, etc. ++

12voto

John Dibling Points 56814

Comme d'autres l'ont dit, vous ne pouvez pas initier un vecteur de la même manière qu'un tableau de style C, à moins que vous ne lui donniez des pointeurs vers un tableau source. Mais dans ce cas, si votre vecteur est un const global, pourquoi ne pas utiliser un vieux tableau C à la place ?

const int MyInts[] = {
1, 2, 3, 4, 5};

const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);

Vous pouvez même utiliser des algorithmes STL sur ce tableau, de la même manière que vous utiliseriez des algorithmes sur un vecteur constant...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);

4 votes

C'est un bon point, mais il le passe à une fonction qui attend un vecteur. Il n'est peut-être pas en mesure de modifier cette fonction.

0 votes

Cela nécessite de suivre la taille du vecteur en externe, ce qui est pénible lorsqu'on a affaire à de nombreux vecteurs. Sinon, cette solution serait la meilleure car elle ne nécessite qu'une seule copie des entiers en mémoire.

6voto

janm Points 9310

Vous pouvez le faire en deux étapes :

namespace {
    const T s_actual_array[] = { ... };
    const std::vector<const T> s_blah(s_actual_array,
        s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
}

Peut-être pas aussi belle que vous le souhaiteriez, mais fonctionnelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X