How-to инициализировать 'const std::vector <T>' как массив c

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

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

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

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

Ответ 1

Вам либо нужно ждать С++ 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;

для С++ 11:

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

Ответ 2

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

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

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

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

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

или

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

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

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

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

Ответ 3

Короткие и грязные пути (похожие на 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"));
}

Теперь у С++ 11 есть списки инициализаторов, поэтому вам не нужно делать это таким образом или даже использовать Boost. Но, например, вы можете сделать выше в С++ 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;
    }
}

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

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

}

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

Как насчет:

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

Ответ 7

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

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

Ответ 8

Не уверен, правильно ли я понял. Я понимаю ваш вопрос следующим образом: вы хотите инициализировать вектор для большого количества элементов. Что неправильно с использованием 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() будут недействительными.

Ответ 9

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

vector<T> vec(num_items, item);

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

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

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

Ответ 10

Основываясь на ответе 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();

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