как инициализировать 'const std :: vector <T>' как массив c - PullRequest
78 голосов
/ 24 октября 2008

Существует ли элегантный способ создания и инициализации const std::vector<const T>, например const T a[] = { ... }, с фиксированным (и небольшим) числом значений? Мне нужно часто вызывать функцию, которая ожидает vector<T>, но в моем случае эти значения никогда не изменятся.

В принципе я думал о чем-то вроде

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

, поскольку v не будет использоваться вне этого модуля компиляции.

Ответы [ 10 ]

60 голосов
/ 24 октября 2008

Вам нужно подождать C ++ 0x или использовать что-то вроде Boost.Assign , чтобы сделать это.

например:.

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

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

для C ++ 11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };
39 голосов
/ 24 октября 2008

Если вы спрашиваете, как инициализировать константный вектор, чтобы он содержал интересное содержимое, тогда ответ, вероятно, заключается в использовании конструктора копирования. Сначала вы старательно заполняете вектор, а затем создаете из него свой новый вектор констант. Или вы можете использовать шаблон конструктора vector (InputIterator, InputIterator) для инициализации из какого-либо другого вида контейнера или массива. Если массив, то это можно было бы определить с помощью списка инициализации.

Что-то вроде этого, надеюсь, близко к тому, что вы хотите:

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

Если вы спрашиваете, как передать константный вектор в функцию, которая принимает вектор, тогда ответ либо:

  • вы не можете, потому что функция может изменить вектор, а ваш объект / ссылка является константой. Сделайте неконстантную копию оригинала и передайте ее.

или

  • используйте const_cast для удаления константности, чтобы передать ее в функцию, которая принимает неконстантный вектор, но вы точно знаете, что это не изменит вектор.

Последнее является одной из тех вещей, которая совершенно справедливо заставит любого, кто ее видит, комментировать очки и тот факт, что они ничего не делают. Это именно то, для чего предназначен const_cast, но есть достаточно сильный аргумент, который говорит, что если вам нужен const_cast, вы уже проиграли.

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

[Edit: только что заметил, что вы говорите о разнице между вектором и константным вектором . К сожалению, в STL vector и vector являются совершенно не связанными типами, и единственный способ преобразования между ними - копирование. Это разница между векторами и массивами - T ** может быть тихо и безопасно преобразован в const T * const *]

15 голосов
/ 31 октября 2008

Короткий и грязный путь (аналогично Boost's 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"));
}

Теперь в C ++ 11 есть списки инициализаторов, поэтому вам не нужно делать это таким образом или даже использовать Boost. Но, в качестве примера, вы можете сделать вышеперечисленное в C ++ 11 более эффективно, например:

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

Но это все же не так эффективно, как использование списка инициализатора C ++ 11, поскольку для вектора не определен оператор = (vlist_of &&).

способ tjohns20, модифицированный следующим образом, может быть лучше 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;
    }

}
12 голосов
/ 24 октября 2008

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

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

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

Вы можете даже использовать алгоритмы STL для этого массива так же, как вы используете алгоритмы для константного вектора ...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
6 голосов
/ 24 октября 2008

Вы можете сделать это в два этапа:

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

Возможно, не так красиво, как хотелось бы, но функционально.

5 голосов
/ 04 мая 2012

Как насчет:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
3 голосов
/ 15 января 2014

Старый вопрос, но я столкнулся с той же проблемой сегодня, вот подход, который был наиболее приемлем для моих целей:

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

Пример, чтобы избежать чрезмерного копирования:

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;
}
0 голосов
/ 09 мая 2011

Основываясь на ответе Shadow2531, я использую этот класс для инициализации векторов, фактически не наследуя от std :: vector, как это делало решение 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;
};

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

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

По сравнению с решением Стива Джессопа он создает намного больше кода, но если создание массива не критично для производительности, я считаю, что это хороший способ инициализировать массив в одну строку

0 голосов
/ 24 октября 2008

Если они все одинаковые, вы можете просто сделать

vector<T> vec(num_items, item);

но я предполагаю, что это не так - в этом случае, вероятно, самый лучший способ:

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

C ++ 0x позволит вам использовать список инициализаторов именно так, как вы думаете, но это к сожалению, не так много хорошего.

0 голосов
/ 24 октября 2008

Не уверен, правильно ли я вас понял. Я понимаю ваш вопрос так: вы хотите инициализировать вектор большим количеством элементов. Что не так с использованием push_back () для вектора? : -)

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

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

Остерегайтесь добавлять больше, чем следующая степень 2 элементов, даже если используете push_back (). Указатели на v.begin () будут недействительными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...