как инициализировать 'const std :: vector <T>' как массив переменного тока


79

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

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

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

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

Ответы:


61

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

1
«Должен» может быть немного сильным; двухэтапный подход также может работать. Хотя бустовый подход, безусловно, хорош.
— janm

Geussing, вы имели в виду: #include <boost / assign / std / vector.hpp>
— Зак,

1
@Jason: boost.assign определяет шаблонные перегрузки оператора + = и оператора в векторе <T>
— Ферруччо

но как перехватить каждое из значений запятой?
— Jason S

41
12345? Такая же комбинация у меня в багаже!
— JRG

39

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

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

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

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

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

или же

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

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

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

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


Если я не хочу копировать t1, t2, t3, как я могу сделать это лучше всего? Это единственное / лучшее решение, которое я нашел до сих пор: <code> const int * p = & ra [0]; вектор <const int *> v; for (int i = 0; i <size; ++ i, ++ p) v.push_back (p); </code>
— AudioDroid

@AudioDroid: да, нет возможности поместить что-либо в вектор, не скопировав его (или переместив, в C ++ 11), поэтому ближе всего вы получите вектор указателей (или интеллектуальных указателей). Но если у вас уже есть три объекта в массиве, то обычно нет смысла создавать на них вектор указателей: просто используйте массив для того, для чего вы бы использовали этот вектор.
— Стив Джессоп

15

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

Теперь в 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, потому что operator=(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

Как говорили другие, вы не можете инициализировать вектор так же, как вы можете инициализировать массив в стиле 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);

4
Это хороший момент, но он передает его функции, которая ожидает вектор. Возможно, он не сможет изменить эту функцию.
— Ферруччо,

Это требует внешнего отслеживания размера вектора, что является проблемой при работе со многими векторами. В противном случае это решение было бы лучшим, поскольку для него требуется только одна копия целых чисел в памяти.
— MasterHD

6

Сделать это можно в два этапа:

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

Как насчет:

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

Неважно, Стив Джессоп уже указал на этот подход.
— opal

3

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

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

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

vector<T> vec(num_items, item);

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

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

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


0

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

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

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


С этим кодом много проблем. Вы не можете просто вызвать vector :: reserve (), а затем начать манипулировать указателями и в зависимости от деталей магической реализации, таких как степени двойки. Итератор преобразован в указатель? А как насчет vector :: size ()? На каком компиляторе и библиотеке STL это вообще компилируется?
— janm

Я согласен, это очень плохой код. Лучшее, вам следует забыть об использовании указателя / итератора и вернуться к push_back
— paercebal

Нет, это совершенно законный код на C ++. STL гарантирует, что это сработает: вектор будет выделять последовательное пространство как минимум для 2 ^ n элементов. После всех 2 ^ n элементов vector разрешено перераспределить, что дает вам новое пространство за vector :: begin (). Извините, это может быть хакерство, но это стандарт. :-)
— mstrobl

1
... однако я глубоко не убежден, что использование гарантии непрерывной памяти для добавления новых значений по указателю является стандартным - для начала, у вектора нет возможности обновить свой собственный размер, поэтому я почти уверен, что результаты этого кода либо не определены, либо являются вектором размера 0.
— Стив Джессоп,

1
Конечно, вы можете использовать непрерывное пространство в векторе; стандарт гарантирует это. Тем не менее, вызова reserve () для этого недостаточно; вы должны добавить элементы так, чтобы size () был правильным. Вы можете изменить код, чтобы выделить правильное количество элементов ....
— янм,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.