C ++, скопировать в вектор - PullRequest
127 голосов
/ 17 февраля 2011

Мне нужно скопировать 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

В чем проблема?

Ответы [ 6 ]

201 голосов
/ 17 февраля 2011

Вам необходимо использовать 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()); 
111 голосов
/ 17 февраля 2011

Просто используйте конструктор для вектора, который принимает итераторы:

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

Предполагается, что вам просто нужно содержимое s в v, и в v нет ничего до копирования данных в него.

34 голосов
/ 08 мая 2014

вот еще один вариант использования vector::assign:

theVector.assign(theSet.begin(), theSet.end());
23 голосов
/ 17 февраля 2011

Вы не зарезервировали достаточно места в своем векторном объекте для хранения содержимого вашего набора.

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());
1 голос
/ 12 июня 2019

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

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

Таким образом, мы будем вызывать конструктор копирования только для каждого элемента, а не сначала вызывать конструктор по умолчанию, а затем оператор назначения копирования длядругие решения, перечисленные выше.Дополнительные пояснения приведены ниже.

  1. back_inserter может использоваться, но это вызовет push_back () для вектора (https://en.cppreference.com/w/cpp/iterator/back_insert_iterator). emplace_back () более эффективен, потому что избегает создания временного при использовании push_back () . Это не проблема с тривиально сконструированными типами, но будет влиять на производительность для нетривиально сконструированных типов (например, std :: string).

  2. Нам нужно избегать создания вектора с аргументом размера, который приводит к построению всех элементов по умолчанию (ни за что). Как и в случае решения с использованием std :: copy () , например.

  3. И, наконец, метод vector :: assign () или конструктор, принимающий диапазон итератора, не являются хорошими вариантами, потому что они будут вызыватьstd :: distance () (чтобы узнать количество элементов) на итераторах set . Это приведет к нежелательной дополнительной итерации по всем элементам set , поскольку набор представляет собой структуру данных Binary Search Treeи он не реализует итераторы произвольного доступа.

Надеюсь, это поможет.

1 голос
/ 17 февраля 2011

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())); 
...