Есть ли элегантный способ создать и инициализировать const std::vector<const T>
лайк const T a[] = { ... }
для фиксированного (и небольшого) количества значений?
Мне нужно часто вызывать функцию, которая ожидает a vector<T>
, но в моем случае эти значения никогда не изменятся.
В принципе придумал что-то вроде
namespace {
const std::vector<const T> v(??);
}
поскольку v не будет использоваться за пределами этой единицы компиляции.
Если вы спрашиваете, как инициализировать константный вектор, чтобы он имел интересное содержимое, ответ, вероятно, заключается в использовании конструктора копирования. Сначала вы кропотливо заполняете вектор, а затем создаете из него новый вектор const. Или вы можете использовать
vector<InputIterator>(InputIterator, InputIterator)
шаблон конструктора для инициализации из другого типа контейнера или массива. Если массив, то это можно было бы определить с помощью списка инициализации.Что-то вроде этого, надеюсь, близко к тому, что вы хотите:
const T ra[3] = {t1, t2, t3}; const vector<const T> v(ra, ra+3);
Если вы спрашиваете, как передать константный вектор в функцию, которая принимает вектор, тогда ответ будет либо:
или же
const_cast
для удаления константы, чтобы передать ее в функцию, которая принимает неконстантный вектор, но которая, как вы случайно знаете, не будет изменять вектор.Последнее - одна из тех вещей, которые совершенно справедливо заставят любого, кто увидит это, сделать комментарии по поводу очков и того факта, что они ничего не делают. Это именно то
const_cast
, для чего нужноconst_cast
, но есть достаточно веский аргумент, который говорит, что если вам нужно , вы уже проиграли.Выполнение обеих этих вещей (создание константного вектора из неконстантного с помощью конструктора копирования, а затем отбрасывание константности) определенно неверно - вы должны были просто использовать неконстантный вектор. Так что выберите самое большее из этих вариантов ...
[ Edit: только что заметил, что вы говорите о разнице между
vector<T>
иconst vector<const T>
. К сожалению, в STL этоvector<const T>
иvector<T>
совершенно не связанные типы, и единственный способ конвертировать между ними - копировать. В этом разница между векторами и массивами - aT**
можно незаметно и безопасно преобразовать вconst T *const *
]источник
Короткий и грязный путь (похож на 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; } }
источник
Как говорили другие, вы не можете инициализировать вектор так же, как вы можете инициализировать массив в стиле 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);
источник
Сделать это можно в два этапа:
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]))); }
Возможно, не такой красивый, как хотелось бы, но функциональный.
источник
Как насчет:
int ar[]={1,2,3,4,5,6}; const int TotalItems = sizeof(ar)/sizeof(ar[0]); std::vector<int> v(ar, ar+TotalItems);
источник
Старый вопрос, но сегодня я столкнулся с той же проблемой, вот подход, который был наиболее приемлем для моих целей:
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; }
источник
Если они все одинаковы, ты можешь просто сделать
vector<T> vec(num_items, item);
но я предполагаю, что это не так - и в этом случае, вероятно, самый простой способ:
vector<T> vec(num_items); vec[0] = 15; vec[1] = 5; ...
C ++ 0x позволит вам использовать список инициализаторов именно так, как вы думаете, но, к сожалению, сейчас это не очень хорошо.
источник
Основываясь на ответе 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();
По сравнению с решением Стива Джессопа он создает намного больше кода, но если создание массива не критично для производительности, я считаю, что это хороший способ инициализировать массив в одной строке
источник
Не уверен, правильно ли я вас понял. Я понимаю ваш вопрос так: вы хотите инициализировать вектор для большого количества элементов. Что плохого в использовании
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()
Тогда указатели на будут недействительными.источник