С++, скопировать в вектор
Мне нужно скопировать std::set
в std::vector
:
std::set <double> input;
input.insert(5);
input.insert(6);
std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable
Где проблема?
Ответы
Ответ 1
Вам нужно использовать back_inserter
:
std::copy(input.begin(), input.end(), std::back_inserter(output));
std::copy
не добавляет элементы в контейнер, в который вы вставляете: он не может; он имеет только итератор в контейнер. Из-за этого, если вы передаете выходный итератор непосредственно на std::copy
, вы должны убедиться, что он указывает диапазон, который по крайней мере достаточно велик, чтобы удерживать диапазон ввода.
std::back_inserter
создает выходной итератор, который вызывает push_back
в контейнере для каждого элемента, поэтому каждый элемент вставляется в контейнер. В качестве альтернативы вы могли бы создать достаточное количество элементов в std::vector
для хранения скопированного диапазона:
std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());
Или вы можете использовать конструктор диапазона std::vector
:
std::vector<double> output(input.begin(), input.end());
Ответ 2
Просто используйте конструктор для вектора, который принимает итераторы:
std::set<T> s;
//...
std::vector v( s.begin(), s.end() );
Предполагается, что вы просто хотите, чтобы содержимое s в v, и ничего не было в v перед копированием данных на него.
Ответ 3
здесь другая альтернатива, использующая vector::assign
:
theVector.assign(theSet.begin(), theSet.end());
Ответ 4
Вы не зарезервировали достаточное пространство в своем векторном объекте для хранения содержимого вашего набора.
std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());
Ответ 5
std::copy
не может использоваться для вставки в пустой контейнер. Для этого вам необходимо использовать insert_iterator следующим образом:
std::set<double> input;
input.insert(5);
input.insert(6);
std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin()));
Ответ 6
Я думаю, что самый эффективный способ - это предварительно выделить, а затем использовать элементы:
template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)
{
std::vector<T> to;
to.reserve(from.size());
for (auto const& value : from)
to.emplace_back(value);
return to;
}
Таким образом, мы будем вызывать конструктор копирования только для каждого элемента, а не сначала вызывать конструктор по умолчанию, а затем копировать оператор присваивания для других решений, перечисленных выше. Больше разъяснений ниже.
-
back_inserter может использоваться, но он вызовет push_back() для вектора (https://en.cppreference.com/w/cpp/iterator/back_insert_iterator). emplace_back() более эффективен, потому что избегает создания временного при использовании push_back(). Это не проблема с тривиально сконструированными типами, но будет влиять на производительность для нетривиально сконструированных типов (например, std :: string).
-
Нам нужно избегать создания вектора с аргументом размера, который приводит к созданию всех элементов по умолчанию (ни за что). Как, например, с решением с использованием std :: copy().
-
И, наконец, метод vector :: assign() или конструктор, принимающий диапазон итераторов, не являются хорошими вариантами, потому что они будут вызывать std :: distance() (чтобы узнать количество элементов) на итераторах множества. Это приведет к нежелательной дополнительной итерации по всем элементам набора, поскольку набор представляет собой структуру данных Binary Search Tree и не реализует итераторы с произвольным доступом.
Надеюсь, это поможет.