Как проще всего инициализировать std :: vector жестко закодированными элементами?


612

Я могу создать массив и инициализировать его так:

int a[] = {10, 20, 30};

Как мне создать std::vectorи инициализировать его так же элегантно?

Лучший способ, который я знаю, это:

std::vector<int> ints;

ints.push_back(10);
ints.push_back(20);
ints.push_back(30);

Есть ли способ лучше?


1
если вы не собираетесь изменять размер целых чисел после инициализации, рассмотрите возможность использования массива tr1.
зр.

@zr, ты мне любопытен ... если бы мне нужен фиксированный размер, не мог бы я использовать сами старые массивы? Глядя на массив tr1 прямо сейчас ...
Агнель Курьян

2
tr1::arrayполезно, потому что обычные массивы не предоставляют интерфейс контейнеров STL
Мануэль

Изменил заголовок, чтобы сделать это явно вопросом C ++ 03. Это казалось проще, чем проходить и исправлять все ответы, чтобы иметь смысл с новым стандартом C ++.
TED

Ответы:


548

Одним из методов будет использование массива для инициализации вектора

static const int arr[] = {16,2,77,29};
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );

7
@Agnel Он будет работать нормально без staticили const, однако они оба делают его более понятным относительно того, как его следует использовать, и позволяют компилятору вносить дополнительные оптимизации.
Якоби

68
Я не отрицал это, но я был искушен. Главным образом потому, что это практически ничего не спасает от использования инициализированного массива. Однако, это действительно ошибка C ++, а не ваша.
TED

2
Можете ли вы объяснить, почему вы используете эти параметры при определении вектора vec?
DomX23

13
sizeof (массив) - одно из немногих исключений, которое позволяет получить общий размер элементов массива, а НЕ размерность указателя arr. Поэтому в основном он использует вектор (pointer_to_first_element, pointer_to_first_element + size_in_bytes_of_the_whole_array / size_of_one_element), который представляет собой: vector (pointer_to_first_element, pointer_after_final_element). Тип уже указан с <int>, поэтому вектор знает, сколько стоит один элемент. Помните, что итераторы можно рассматривать как указатели, поэтому вы в основном используете конструктор vector (начало итератора, конец итератора)
Джонни Полинг,

11
@TED: Иногда вам нужно изменить полученный вектор. Например, вам может потребоваться всегда иметь некоторые параметры по умолчанию и иногда добавлять несколько настроенных к ним.
DarkWanderer

642

Если ваш компилятор поддерживает C ++ 11, вы можете просто сделать:

std::vector<int> v = {1, 2, 3, 4};

Это доступно в GCC начиная с версии 4.4 . К сожалению, VC ++ 2010 в этом отношении, похоже, отстает.

В качестве альтернативы библиотека Boost.Assign использует немакро -магию, чтобы разрешить следующее:

#include <boost/assign/list_of.hpp>
...
std::vector<int> v = boost::assign::list_of(1)(2)(3)(4);

Или:

#include <boost/assign/std/vector.hpp>
using namespace boost::assign;
...
std::vector<int> v;
v += 1, 2, 3, 4;

Но имейте в виду , что это имеет некоторые накладные расходы ( в основном, list_ofстроит std::dequeпод капотом) , поэтому для повышения производительности критически важных код , который вы бы лучше делать , как говорит Yacoby.


Поскольку векторы самоконтролируемы, можно ли инициализировать их как пустые? Как и в конструкторе: this->vect = {};?
Azurespot

3
@Azurespot Вы можете просто инициализировать его, и он будет пустым:std::vector<T> vector;
Лука,

2
На случай, если кому-то будет интересно std::vector<int> v = {1, 2, 3, 4};, вектор initializer list constructorбудет вызван для такого рода инициализации, его документ можно найти в C++ 11разделе .
Симомо

103

Если вы можете, используйте современный C ++ [11,14,17, ...] способ:

std::vector<int> vec = {10,20,30};

Старый способ циклического перебора массива переменной длины или использование sizeof()действительно ужасен для глаз и совершенно не нужен с точки зрения умственных издержек. Тьфу.


2
Справедливости ради, это изначально был вопрос C ++ 03, но я надеюсь, что люди / компании примут новые стандарты. C ++ по-прежнему нуждается в реализации массива переменной длины (VLA) в стандартной библиотеке, аналогичной доступной в Eigen и Boost.
Адам Эриксон

К сожалению, этот подход проблематичен в некоторых случаях, например, open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1467 . Тьфу.
Гонки

Если вам нужна «инициализация списком агрегата из объекта того же типа», возможно, в вашей базе кода есть более серьезные проблемы ... Я не могу представить ни одного приложения, где бы это оправдывало проблемы отладки.
Адам Эриксон

77

В C ++ 0x вы сможете сделать это так же, как с массивом, но не в текущем стандарте.

Только с языковой поддержкой вы можете использовать:

int tmp[] = { 10, 20, 30 };
std::vector<int> v( tmp, tmp+3 ); // use some utility to avoid hardcoding the size here

Если вы можете добавить другие библиотеки, вы можете попробовать boost :: assignment:

vector<int> v = list_of(10)(20)(30);

Чтобы избежать жесткого кодирования размера массива:

// option 1, typesafe, not a compile time constant
template <typename T, std::size_t N>
inline std::size_t size_of_array( T (&)[N] ) {
   return N;
}
// option 2, not typesafe, compile time constant
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

// option 3, typesafe, compile time constant
template <typename T, std::size_t N>
char (&sizeof_array( T(&)[N] ))[N];    // declared, undefined
#define ARRAY_SIZE(x) sizeof(sizeof_array(x))

Конечно, я не понизил голос, но у меня все равно есть вопрос: когда размер массива не является постоянной времени компиляции? Т.е. в каких случаях вы бы использовали первое решение во втором фрагменте, а не третье?
Мануэль

4
@Manuel, размер массива является частью типа, и поэтому является постоянной времени компиляции. Теперь вариант 1 использует эту постоянную времени компиляции 'N' в качестве возвращаемого значения для функции. Возвращение функции - это не время компиляции, а значение времени выполнения, даже если оно, вероятно, будет встроено как постоянное значение в месте вызова. Разница в том, что вы не можете делать int another[size_of_array(array)], тогда как вы можете делать int another[ARRAY_SIZE(array)].
Дэвид Родригес - dribeas

1
В варианте 3: я не понимаю, что вы имеете в виду под "объявленным, неопределенным"? Значит переменная не займет дополнительную память?
To1ne

1
@ To1ne, который на самом деле является объявлением функции, а не переменной. Причина или определение этого в том, что мы на самом деле не хотим использовать функцию для чего-то еще, кроме sizeofвыражения, которое не нуждается в определении. Хотя вы действительно можете предоставить определение, чтобы сделать его правильно, потребуется статическое размещение массива и возвращение ссылки на него, и следующий вопрос будет состоять в том, что будет иметь смысл в качестве значений для массива? (Также обратите внимание, что это означает один массив на комбинацию типа / размера экземпляров функции!) Поскольку для него нет разумного использования, я бы предпочел этого избегать.
Дэвид Родригес - dribeas

1
@mhd: Вы не можете создать пустой массив на языке. 'int arr [0] = {};' не является допустимым кодом C ++. Но вы правы, что если вы хотите инициализировать пустой вектор и непустой вектор, вам придется использовать разные конструкции. Начиная с C ++ 11, это не проблема, так как вы можете использовать конструктор списка инициализаторов
David Rodríguez - dribeas

61

В C ++ 11:

#include <vector>
using std::vector;
...
vector<int> vec1 { 10, 20, 30 };
// or
vector<int> vec2 = { 10, 20, 30 };

Использование boost list_of:

#include <vector>
#include <boost/assign/list_of.hpp>
using std::vector;
...
vector<int> vec = boost::assign::list_of(10)(20)(30);

Используя boost назначить:

#include <vector>
#include <boost/assign/std/vector.hpp>
using std::vector;
...
vector<int> vec;
vec += 10, 20, 30;

Обычный STL:

#include <vector>
using std::vector;
...
static const int arr[] = {10,20,30};
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );

Обычный STL с общими макросами:

#include <vector>
#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0])
#define ARRAY_END(ar) (ar + ARRAY_SIZE(ar))
using std::vector;
...
static const int arr[] = {10,20,30};
vector<int> vec (arr, ARRAY_END(arr));

Обычный STL с макросом инициализатора вектора:

#include <vector>
#define INIT_FROM_ARRAY(ar) (ar, ar + sizeof(ar) / sizeof(ar[0])
using std::vector;
...
static const int arr[] = {10,20,30};
vector<int> vec INIT_FROM_ARRAY(arr);

2
C ++ 11 также поддерживает std::beginи std::endдля массива, поэтому вектор также можно инициализировать как static const int arr[] = {10,20,30}; vector<int> vec(begin(arr), end(arr));.
Jaege

54

Просто подумал, что я добавлю свои 0,02 доллара. Я склонен заявлять об этом:

template< typename T, size_t N >
std::vector<T> makeVector( const T (&data)[N] )
{
    return std::vector<T>(data, data+N);
}

в служебном заголовке где-то, и тогда все, что требуется, это:

const double values[] = { 2.0, 1.0, 42.0, -7 };
std::vector<double> array = makeVector(values);

Но я не могу ждать C ++ 0x. Я застрял, потому что мой код также должен компилироваться в Visual Studio. Бу.


1
Этот метод также может быть использован для перегрузки функции для приема массива с типизированным размером.
Андрес Риофрио

4
Можете ли вы объяснить const T (&data)[N]часть? Как определяется размер массива в вашем вызове makeVector(values)?
Патрик

36

До C ++ 11:

Метод 1 =>

vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
vector<int>v;

Способ 2 =>

 v.push_back(SomeValue);

C ++ 11 и ниже также возможно

vector<int>v = {1, 3, 5, 7};

28

Начиная с:

int a[] = {10, 20, 30}; //i'm assuming a is just a placeholder

Если у вас нет компилятора C ++ 11 и вы не хотите использовать boost:

const int a[] = {10, 20, 30};
const std::vector<int> ints(a,a+sizeof(a)/sizeof(int)); //make it const if you can

Если у вас нет компилятора C ++ 11 и вы можете использовать boost:

#include <boost/assign.hpp>
const std::vector<int> ints = boost::assign::list_of(10)(20)(30);

Если у вас есть компилятор C ++ 11:

const std::vector<int> ints = {10,20,30};

22

Для векторной инициализации -

vector<int> v = {10,20,30}

может быть сделано, если у вас есть компилятор c ++ 11.

Иначе, вы можете иметь массив данных, а затем использовать цикл for.

int array[] = {10,20,30}
for(unsigned int i=0; i<sizeof(array)/sizeof(array[0]); i++)
{
     v.push_back(array[i]);
}

Помимо этого, есть несколько других способов, описанных выше, с использованием некоторого кода. На мой взгляд, эти способы легко запомнить и быстро написать.



16

Я строю свое собственное решение, используя va_arg. Это решение совместимо с C ++ 98.

#include <cstdarg>
#include <iostream>
#include <vector>

template <typename T>
std::vector<T> initVector (int len, ...)
{
  std::vector<T> v;
  va_list vl;
  va_start(vl, len);
  for (int i = 0; i < len; ++i)
    v.push_back(va_arg(vl, T));
  va_end(vl);
  return v;
}

int main ()
{
  std::vector<int> v = initVector<int> (7,702,422,631,834,892,104,772);
  for (std::vector<int>::const_iterator it = v.begin() ; it != v.end(); ++it)
    std::cout << *it << std::endl;
  return 0;
}

демонстрация


14

Если ваш компилятор поддерживает макросы Variadic (что справедливо для большинства современных компиляторов), то вы можете использовать следующий макрос, чтобы превратить инициализацию вектора в однострочник:

#define INIT_VECTOR(type, name, ...) \
static const type name##_a[] = __VA_ARGS__; \
vector<type> name(name##_a, name##_a + sizeof(name##_a) / sizeof(*name##_a))

С помощью этого макроса вы можете определить инициализированный вектор с помощью следующего кода:

INIT_VECTOR(int, my_vector, {1, 2, 3, 4});

Это создаст новый вектор целых с именем my_vector с элементами 1, 2, 3, 4.


13

Если вы не хотите использовать boost, но хотите насладиться синтаксисом вроде

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

просто включите этот кусок кода

template <class T> class vector_inserter{
public:
    std::vector<T>& v;
    vector_inserter(std::vector<T>& v):v(v){}
    vector_inserter& operator,(const T& val){v.push_back(val);return *this;}
};
template <class T> vector_inserter<T> operator+=(std::vector<T>& v,const T& x){
    return vector_inserter<T>(v),x;
}

1
Я не смог понять, как использовать этот код, но он выглядит интересно.
Даниэль Бакмастер,

Это как один из вышеупомянутых комментариев. Просто перегрузка + = и оператор запятой. Поставим скобки для ясности: ((((v+=1),2),3),4),5) вот как это работает: во-первых, vector<T> += Tвозвращает vector_inserter, который позволяет вызывать его, viкоторый инкапсулирует исходный вектор, затем vi,Tдобавляет T к исходному вектору, который viинкапсулирует и возвращает его самостоятельно, чтобы мы могли сделать это vi,Tснова.
Пити Онгмонгколкул,

этот код не работал правильно на gcc 4.2.1. Я думаю, что из-за возврата ссылки на локальную переменную внутри оператора + =, но идея замечательная. я отредактировал код и появился еще один конструктор копирования. поток теперь -> + = -> ctor -> запятая -> copy -> dtor -> запятая ...... -> запятая -> dtor.
Евгения

Я бы, вероятно, перегружен << вместо + =. По крайней мере, << уже имеет расплывчатые правила побочных эффектов из-за сдвигов битов и cout
Speed8ump

11

В C ++ 11:

static const int a[] = {10, 20, 30};
vector<int> vec (begin(a), end(a));

21
Если вы уже используете C ++ 11, вы можете также пойти на прямой подход - vector<int> arr = {10, 20, 30};.
Бернхард Баркер

На самом деле у меня был входящий int [] (немного C lib) и я хотел вставить в вектор (C ++ lib). Этот ответ помог, остальные не сделали ;-)
Туманность

10

Вы можете сделать это используя boost :: assign.

vector<int> values;  
values += 1,2,3,4,5,6,7,8,9;

подробно здесь


19
Я не видел худшего случая злоупотребления перегрузкой оператора в течение длительного времени. Привязывает ли +=1,2,3,4 .. к концу значений или добавляет 1 к 1-му элементу, 2 к 2-му элементу, 3 к 3-му элементу (такой синтаксис должен быть в MATLAB- как языки)
бобобо

10

Более поздняя копия вопрос имеет этот ответ на Викторе SEHR . Для меня это компактно, визуально привлекательно (похоже, что вы «запихиваете» значения в), не требует c ++ 11 или стороннего модуля и избегает использования дополнительной (записанной) переменной. Ниже, как я использую это с несколькими изменениями. Я могу перейти к расширению функции vector и / или va_arg в будущем intead.


// Based on answer by "Viktor Sehr" on Stack Overflow
// https://stackoverflow.com/a/8907356
//
template <typename T>
class mkvec {
public:
    typedef mkvec<T> my_type;
    my_type& operator<< (const T& val) {
        data_.push_back(val);
        return *this;
    }
    my_type& operator<< (const std::vector<T>& inVector) {
        this->data_.reserve(this->data_.size() + inVector.size());
        this->data_.insert(this->data_.end(), inVector.begin(), inVector.end());
        return *this;
    }
    operator std::vector<T>() const {
        return data_;
    }
private:
    std::vector<T> data_;
};

std::vector<int32_t>    vec1;
std::vector<int32_t>    vec2;

vec1 = mkvec<int32_t>() << 5 << 8 << 19 << 79;  
// vec1 = (5,8,19,79)
vec2 = mkvec<int32_t>() << 1 << 2 << 3 << vec1 << 10 << 11 << 12;  
// vec2 = (1,2,3,5,8,19,79,10,11,12)

7

Ниже методы могут быть использованы для инициализации вектора в C ++.

  1. int arr[] = {1, 3, 5, 6}; vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));

  2. vector<int>v; v.push_back(1); v.push_back(2); v.push_back(3); и так далее

  3. vector<int>v = {1, 3, 5, 7};

Третий разрешен только в C ++ 11 и далее.


5

Здесь есть много хороших ответов, но так как я самостоятельно дошел до того, как прочел это, я решил все равно бросить здесь ...

Вот метод, который я использую для этого, который будет работать универсально на всех компиляторах и платформах:

Создайте структуру или класс в качестве контейнера для вашей коллекции объектов. Определить функцию перегрузки оператора для <<.

class MyObject;

struct MyObjectList
{
    std::list<MyObject> objects;
    MyObjectList& operator<<( const MyObject o )
    { 
        objects.push_back( o );
        return *this; 
    }
};

Вы можете создавать функции, которые принимают вашу структуру в качестве параметра, например:

someFunc( MyObjectList &objects );

Затем вы можете вызвать эту функцию, например так:

someFunc( MyObjectList() << MyObject(1) <<  MyObject(2) <<  MyObject(3) );

Таким образом, вы можете построить и передать динамически измеренную коллекцию объектов в функцию в одну чистую строку!


4

Если вы хотите что-то в том же общем порядке, что и Boost :: assign, не создавая зависимости от Boost, следующее, по крайней мере, примерно похоже:

template<class T>
class make_vector {
    std::vector<T> data;
public:
    make_vector(T const &val) { 
        data.push_back(val);
    }

    make_vector<T> &operator,(T const &t) {
        data.push_back(t);
        return *this;
    }

    operator std::vector<T>() { return data; }
};

template<class T> 
make_vector<T> makeVect(T const &t) { 
    return make_vector<T>(t);
}

Хотелось бы, чтобы синтаксис его использования был чище, он все же не особо ужасен:

std::vector<int> x = (makeVect(1), 2, 3, 4);

4
typedef std::vector<int> arr;

arr a {10, 20, 30};       // This would be how you initialize while defining

Для компиляции используйте:

clang++ -std=c++11 -stdlib=libc++  <filename.cpp>

Вопрос состояния C ++ 03 (не 11)
Майк П

1
Я думаю, что это не указывает 03, когда я ответил на это. Не помню точно, хотя. Тем не менее, это все еще полезный ответ для тех, кто ищет быстрое решение.
Shaveenk

4
// Before C++11
// I used following methods:

// 1.
int A[] = {10, 20, 30};                              // original array A

unsigned sizeOfA = sizeof(A)/sizeof(A[0]);           // calculate the number of elements

                                                     // declare vector vArrayA,
std::vector<int> vArrayA(sizeOfA);                   // make room for all
                                                     // array A integers
                                                     // and initialize them to 0 

for(unsigned i=0; i<sizeOfA; i++)
    vArrayA[i] = A[i];                               // initialize vector vArrayA


//2.
int B[] = {40, 50, 60, 70};                          // original array B

std::vector<int> vArrayB;                            // declare vector vArrayB
for (unsigned i=0; i<sizeof(B)/sizeof(B[0]); i++)
    vArrayB.push_back(B[i]);                         // initialize vArrayB

//3.
int C[] = {1, 2, 3, 4};                              // original array C

std::vector<int> vArrayC;                            // create an empty vector vArrayC
vArrayC.resize(sizeof(C)/sizeof(C[0]));              // enlarging the number of 
                                                     // contained elements
for (unsigned i=0; i<sizeof(C)/sizeof(C[0]); i++)
     vArrayC.at(i) = C[i];                           // initialize vArrayC


// A Note:
// Above methods will work well for complex arrays
// with structures as its elements.

4

Если массив:

int arr[] = {1, 2, 3};
int len = (sizeof(arr)/sizeof(arr[0])); // finding length of array
vector < int > v;
std:: v.assign(arr, arr+len); // assigning elements from array to vector 

4

При написании теста очень удобно создать встроенный вектор без определения переменной, например:

assert(MyFunction() == std::vector<int>{1, 3, 4}); // <- this.

3

В связи с этим вы можете использовать следующее, если хотите, чтобы вектор был полностью готов к быстрому выражению (например, к немедленной передаче в другую функцию):

#define VECTOR(first,...) \
   ([](){ \
   static const decltype(first) arr[] = { first,__VA_ARGS__ }; \
   std::vector<decltype(first)> ret(arr, arr + sizeof(arr) / sizeof(*arr)); \
   return ret;})()

пример функции

template<typename T>
void test(std::vector<T>& values)
{
    for(T value : values)
        std::cout<<value<<std::endl;
}

пример использования

test(VECTOR(1.2f,2,3,4,5,6));

хотя будьте осторожны с типом decl, убедитесь, что первое значение точно соответствует желаемому.


3

Есть несколько способов жестко закодировать вектор, я поделюсь несколькими способами:

  1. Инициализация нажатием значений по одному
// Create an empty vector 
    vector<int> vect;  

    vect.push_back(10); 
    vect.push_back(20); 
    vect.push_back(30); 
  1. Инициализация как массивов
vector<int> vect{ 10, 20, 30 };
  1. Инициализация из массива
    int arr[] = { 10, 20, 30 }; 
    int n = sizeof(arr) / sizeof(arr[0]); 

    vector<int> vect(arr, arr + n); 
  1. Инициализация из другого вектора
    vector<int> vect1{ 10, 20, 30 }; 

    vector<int> vect2(vect1.begin(), vect1.end()); 

2

«Как мне создать вектор STL и инициализировать его, как указано выше? Каков наилучший способ сделать это с минимальными усилиями при печати?»

Самый простой способ инициализировать вектор при инициализации встроенного массива - использовать список инициализаторов, который был представлен в C ++ 11 .

// Initializing a vector that holds 2 elements of type int.
Initializing:
std::vector<int> ivec = {10, 20};


// The push_back function is more of a form of assignment with the exception of course
//that it doesn't obliterate the value of the object it's being called on.
Assigning
ivec.push_back(30);

ivec имеет размер 3 элемента после выполнения Assigning (помеченный оператор).


В аналогичных строках я пытаюсь инициализировать карту, std :: map <int, bool> catinfo = {{1, false}}; Но затем получите эту ошибку: в C ++ 98 'catinfo' должна быть инициализирована конструктором, а не '{...}'
pdk

2

Б. Страуструп описывает хороший способ цепочки операций в 16.2.10 Selfreference на стр. 464 в Prog версии C ++ 11. Lang. где функция возвращает ссылку, здесь измененную на вектор. Таким образом, вы можете цепляться как, v.pb(1).pb(2).pb(3);но может быть слишком много работы для такой маленькой выгоды.

#include <iostream>
#include <vector>

template<typename T>
class chain
{
private:
    std::vector<T> _v;
public:
    chain& pb(T a) {
        _v.push_back(a);
        return *this;
    };
    std::vector<T> get() { return _v; };
};

using namespace std;

int main(int argc, char const *argv[])
{
    chain<int> v{};

    v.pb(1).pb(2).pb(3);

    for (auto& i : v.get()) {
        cout << i << endl;
    }

    return 0;
}

1
2
3


Библиотека armadillo делает это для инициализации матрицы, но использует оператор << вместо именованной функции: arma.sourceforge.net/docs.html#element_initialisation
Агнел Куриан

0

Самый простой, более эргономичный способ (с C ++ 11 или новее):

auto my_ints = {1,2,3};

0

Если вы хотите иметь его в своем классе:

#include <initializer_list>
Vector<Type>::Vector(std::initializer_list<Type> init_list) : _size(init_list.size()),
_capacity(_size),
_data(new Type[_size])
{
    int idx = 0;
    for (auto it = init_list.begin(); it != init_list.end(); ++it)
        _data[idx++] = *it;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.