web-dev-qa-db-fra.com

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

Existe-t-il un moyen élégant de créer et d’initialiser un const std::vector<const T> comme const T a[] = { ... } avec un nombre fixe (et petit) 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, j'ai pensé à quelque chose comme

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

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

78
vscharf

Vous devez soit attendre C++ 0x ou 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 };
60
Ferruccio

Si vous demandez comment initialiser un vecteur const de manière à ce qu'il ait un contenu intéressant, la réponse consiste probablement à utiliser le constructeur de copie. D'abord, vous complétez péniblement un vecteur, puis vous créez votre nouveau vecteur const. Ou vous pouvez utiliser le modèle de constructeur vectoriel <InputIterator> (InputIterator, InputIterator) pour initialiser à partir d'un autre type de conteneur ou d'un tableau. S'il s'agissait d'un tableau, cela aurait pu être défini avec une liste d'initialisation.

Espérons que cela ressemble à 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 const dans une fonction qui prend un vecteur, la réponse est soit:

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

ou

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

Ce dernier est l’une des choses qui, à juste titre, obligeront tous ceux qui le verront à faire des commentaires sur les lunettes de protection et le fait qu’ils ne font rien. C'est exactement ce à quoi sert const_cast, mais il existe 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 const à partir d'un non-const avec le constructeur de la copie, puis jeter la constance) est définitivement faux - vous auriez dû simplement utiliser un vecteur non-const. Alors choisissez au plus un de ceux-ci à faire ...

[Edit: vous venez de remarquer que vous parlez d’une différence entre le vecteur <T> et le vecteur const <const T>. Malheureusement, dans la STL, le vecteur <const T> et le vecteur <T> sont des types complètement indépendants, et le seul moyen de convertir ceux-ci consiste à les copier. C'est une différence entre les vecteurs et les tableaux - un T ** peut être converti en silence et en toute sécurité en const T * const *]

39
Steve Jessop

Manière courte et sale (semblable à la liste_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'initialiseurs, vous n'avez donc pas besoin de le faire de cette façon ni même d'utiliser Boost. Mais, à titre d'exemple, vous pouvez faire ce qui précède en C++ 11 plus efficacement, 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 d'utiliser une liste d'initialiseurs C++ 11 car il n'y a pas d'opérateur = (vlist_of &&) défini pour le vecteur.

le chemin de tjohns20 modifié comme suit pourrait être un meilleur c ++ 11 vlist_of:

#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;
    }

}
15
Shadow2531

Comme d'autres l'ont dit, vous ne pouvez pas initier un vecteur de la même manière que vous pouvez initialiser un tableau de style C, à moins de lui donner des pointeurs sur un tableau source. Mais dans ce cas, si votre vecteur est un const global, pourquoi ne pas simplement utiliser un ancien tableau de style 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 contre ce tableau, de la même manière que vous utiliseriez des algorithmes contre un vecteur const ...

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

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 beau que vous voudriez, mais fonctionnel.

6
janm

Que diriez-vous:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
5
opal

Vieille question, mais j'ai rencontré le même problème aujourd'hui, voici l'approche qui était la plus acceptable pour mes fins:

vector<int> initVector(void)
{
    vector<int> initializer;
    initializer.Push_back(10);
    initializer.Push_back(13);
    initializer.Push_back(3);
    return intializer;
}

int main()
{
    const vector<int> a = initVector();
    return 0;
}

Exemple pour éviter les copies excessives:

vector<int> & initVector(void)
{
    static vector<int> initializer;
    if(initializer.empty())
    {
        initializer.Push_back(10);
        initializer.Push_back(13);
        initializer.Push_back(3);
    }
    return intializer;
}

int main()
{
    const vector<int> & a = initVector();
    return 0;
}
3
Kevin

Je ne sais pas si je vous ai bien compris. Je comprends votre question comme ceci: vous voulez initialiser un vecteur sur un grand nombre d’éléments. Quel est le problème avec l'utilisation de Push_back () sur le vecteur? :-)

Si vous connaissez le nombre d'éléments à stocker (ou si vous êtes sûr qu'il en conservera moins que la puissance 2 suivante), vous pouvez le faire si vous avez un vecteur de pointeurs de type X (ne fonctionne qu'avec des pointeurs):

std::vector< X* > v;
v.reserve(num_elems);
X* p = v.begin();
for (int count = 0; count < num_elems; count++)
   p[count] = some_source[count];

Attention à ne pas ajouter plus que la puissance suivante de 2 éléments, même si vous utilisez Push_back (). Les pointeurs sur v.begin () seront alors invalides.

0
mstrobl

Si elles sont toutes identiques, vous pouvez simplement faire

vector<T> vec(num_items, item);

mais je suppose qu'ils ne le sont pas. Dans ce cas, la méthode la plus simple est probablement:

vector<T> vec(num_items);
vec[0] = 15;
vec[1] = 5;
...

C++ 0x vous permettra d'utiliser une liste d'initialiseurs exactement comme vous le souhaitez, mais ce n'est Pas très bon en ce moment, malheureusement.

0
Peter

Sur la base de la réponse de Shadow2531, j'utilise cette classe pour initialiser des vecteurs, sans hériter de std :: vector comme le faisait la solution de Shadow.

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.Push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.Push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

Usage:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

Comparé à la solution de Steve Jessop, il crée beaucoup plus de code, mais si la création de la matrice n’est pas critique en termes de performances, je la trouve un moyen agréable d’initialiser une matrice sur une seule ligne

0
tjohns20