cómo inicializar 'const std :: vector <T>' como una matriz de CA

79

¿Existe una forma elegante de crear e inicializar un me const std::vector<const T>gusta const T a[] = { ... }para un número fijo (y pequeño) de valores?
Necesito llamar a una función con frecuencia que espera una vector<T>, pero estos valores nunca cambiarán en mi caso.

En principio pensé en algo como

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

ya que v no se utilizará fuera de esta unidad de compilación.

vscharf
fuente

Respuestas:

61

Tienes que esperar a C ++ 0x o usar algo como Boost.Assign para hacer eso.

p.ej:

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

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

para C ++ 11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };
Ferruccio
fuente
1
"Tener que" puede ser un poco fuerte; el enfoque de dos pasos también puede funcionar. Aunque el enfoque de impulso es ciertamente bueno.
JANM
Geussing quiso decir: #include <boost / assign / std / vector.hpp>
Zac
1
@Jason: boost.assign define sobrecargas de plantilla de operator + = y operator, en el vector <T>
Ferruccio
pero ¿cómo interceptas cada uno de los valores de coma?
Jason S
41
12345? ¡Tengo la misma combinación en mi equipaje!
JRG
39

Si está preguntando cómo inicializar un vector constante para que tenga contenidos interesantes, entonces la respuesta probablemente sea usar el constructor de copia. Primero, llena laboriosamente un vector, luego crea su nuevo vector constante a partir de él. O puede usar elvector<InputIterator>(InputIterator, InputIterator) plantilla constructor para inicializar desde algún otro tipo de contenedor o matriz. Si es una matriz, entonces podría haberse definido con una lista de inicialización.

Es de esperar que algo como esto se acerque a lo que desea:

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

Si está preguntando cómo pasar un vector constante a una función que toma un vector, la respuesta es:

  • no puede, porque la función podría alterar el vector y su objeto / referencia es constante. Haga una copia no constante del original y transmítala.

o

  • use const_castpara eliminar la constness con el fin de pasarla a una función que toma un vector no constante pero que usted sabe que no modificará el vector.

Esto último es una de esas cosas que, con toda razón, hará que cualquiera que lo vea haga comentarios sobre las gafas y el hecho de que no hacen nada. Para eso const_castes exactamente , pero hay un argumento razonablemente fuerte que dice que si lo necesita const_cast, ya ha perdido.

Hacer ambas cosas (crear un vector constante a partir de uno que no sea constante con el constructor de copia y luego desechar la constante) es definitivamente incorrecto; debería haber usado un vector no constante. Así que elige como máximo uno de estos para hacer ...

[ Editar: acabo de notar que estás hablando de una diferencia entre vector<T>y const vector<const T>. Desafortunadamente en el STL, vector<const T>y vector<T>son tipos completamente no relacionados, y la única forma de convertir entre ellos es copiando. Esta es una diferencia entre vectores y matrices: a T**se puede convertir de forma silenciosa y segura a const T *const *]

Steve Jessop
fuente
Si no quiero copiar t1, t2, t3, ¿cómo puedo hacerlo mejor? Esta es la única / mejor solución que encontré hasta ahora: <code> const int * p = & ra [0]; vector <const int *> v; for (int i = 0; i <size; ++ i, ++ p) v.push_back (p); </code>
AudioDroid
@AudioDroid: sí, no hay forma de poner nada en un vector sin copiarlo (o moverlo, en C ++ 11), así que lo más cercano que obtendrá es un vector de punteros (o punteros inteligentes). Pero si ya tiene sus tres objetos en una matriz, entonces normalmente no tiene sentido crear un vector de punteros a ellos: simplemente use la matriz para lo que sea para lo que usaría el vector.
Steve Jessop
15

Camino corto y sucio (similar al de Boost list_of())

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

Ahora, C ++ 11 tiene listas de inicializadores, por lo que no es necesario que lo haga de esa manera ni siquiera use Boost. Pero, como ejemplo, puede hacer lo anterior en C ++ 11 de manera más eficiente de esta manera:

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

Pero todavía no es tan eficiente como usar una lista de inicializadores de C ++ 11 porque no hay operator=(vlist_of&&)un vector definido.

La forma de tjohns20 modificada como la siguiente podría ser un mejor 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;
    }
    
}
Sombra2531
fuente
perverso, pero útil para las pruebas, etc. ++
Eli Bendersky
12

Como han dicho otros, no puede iniciar un vector de la misma manera que puede iniciar una matriz de estilo C, a menos que le dé punteros a una matriz de origen. Pero en ese caso, si su vector es una constante global, ¿por qué no usar una antigua matriz de estilo C?

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

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

Incluso puede usar algoritmos STL contra esta matriz, de la misma manera que usaría algoritmos contra un vector constante ...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
John Dibling
fuente
4
Ese es un buen punto, pero lo está pasando a una función que espera un vector. Es posible que no pueda modificar esa función.
Ferruccio
Esto requiere rastrear el tamaño del vector externamente, lo cual es un problema cuando se trata de muchos vectores. De lo contrario, esta solución sería la mejor, ya que solo requiere una copia de los números enteros en la memoria.
MasterHD
6

Puedes hacerlo en dos pasos:

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])));
}

Quizás no sea tan hermoso como le gustaría, pero funcional.

Janm
fuente
5

Qué tal si:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
ópalo
fuente
No importa, "Steve Jessop" ya ha señalado este enfoque.
ópalo
3

Antigua pregunta, pero me encontré con el mismo problema hoy, este es el enfoque que fue más aceptable para mis propósitos:

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

Ejemplo para evitar copias excesivas:

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;
}
Kevin
fuente
0

Si son todos iguales, puedes hacerlo

vector<T> vec(num_items, item);

pero supongo que no lo son, en cuyo caso la forma más ordenada es probablemente:

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

C ++ 0x le permitirá usar una lista de inicializadores exactamente de la forma en que está pensando, pero eso no es muy bueno en este momento, desafortunadamente.

Pedro
fuente
0

Según la respuesta de Shadow2531, estoy usando esta clase para inicializar vectores, sin heredar realmente de std :: vector como lo hizo la solución 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;
};

Uso:

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

En comparación con la solución de Steve Jessop, crea mucho más código, pero si la creación de la matriz no es crítica para el rendimiento, me parece una buena forma de inicializar una matriz en una sola línea.

tjohns20
fuente
0

No estoy seguro de si te entendí bien. Entiendo su pregunta de esta manera: desea inicializar un vector en una gran cantidad de elementos. ¿Qué tiene de malo usar push_back()en el vector? :-)

Si conoce la cantidad de elementos a almacenar (o está seguro de que almacenará menos que la próxima potencia de 2) puede hacer esto, si tiene un vector de punteros de tipo X (funciona solo con punteros):

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];

Tenga cuidado de agregar más de la siguiente potencia de 2 elementos, incluso si se usa push_back(). v.begin()Entonces, los punteros a no serán válidos.

mstrobl
fuente
Hay muchos problemas con este código. No puede simplemente llamar a vector :: reserve () y luego comenzar a manipular punteros y dependiendo de los detalles de implementación mágica como poderes de dos. ¿Iterador convertido en puntero? ¿Qué pasa con vector :: size ()? ¿En qué compilador y biblioteca STL se compila esto?
JANM
Estoy de acuerdo que este es un código muy malo. A lo mejor, debe olvidar el uso del puntero / iterador y retroceder a push_back
paercebal
No, eso es código C ++ perfectamente legal. El STL garantiza que esto funcionará: el vector asignará espacio consecutivo para al menos 2 ^ n elementos. Después de todos los 2 ^ n elementos, el vector puede reasignarse, lo que le brinda un nuevo espacio detrás de vector :: begin (). Lo siento, puede ser hacky, pero es el estándar. :-)
mstrobl
1
... sin embargo, no estoy muy convencido de que usar la garantía de memoria contigua para agregar nuevos valores por puntero sea estándar; para empezar, el vector no tiene la oportunidad de actualizar su propio tamaño, así que estoy bastante seguro de los resultados de este código no están definidos o son un vector de tamaño 0.
Steve Jessop
1
Por supuesto, puede aprovechar el espacio contiguo en el vector; el estándar lo garantiza. Sin embargo, llamar a reserve () no es suficiente para hacer eso; debe agregar los elementos de manera que size () sea correcto. Puede cambiar el código para que haya asignado la cantidad correcta de elementos ....
enero