std :: back_inserter для std :: set?

98

Думаю, это простой вопрос. Мне нужно сделать что-то вроде этого:

std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());

Конечно, std::back_inserterне работает, раз нет push_back. std::inserterтоже нужен итератор? Я не использовал, std::inserterпоэтому не знаю, что делать.

Есть у кого-нибудь идеи?


Конечно, мой другой вариант - использовать вектор для s2, а потом просто отсортировать его. Может так лучше?

rlbond
источник

Ответы:

143

setне имеет, push_backпотому что положение элемента определяется компаратором набора. Используйте std::inserterи передайте .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(), 
          std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

Затем итератор вставки вызовет, s2.insert(s2.begin(), x)где xзначение, переданное итератору при записи в него. Набор использует итератор как подсказку, куда вставить. Вы могли бы также использовать s2.end().

Йоханнес Шауб - litb
источник
2
Так как inserter(vec, vec.end())работает и для векторов, зачем вообще кто-то использует back_inserter?
NHDaly 08
7
@NHDaly: потому что back_inserter быстрее
marton78
@ marton78 Но разве это не должно быть быстрее с очень небольшим отрывом, если вообще? Вызов insertвместо push_backвектора должен быть примерно идентичным (O (1)), когда нет необходимости перемещать элементы.
Феликс Домбек
3
@FelixDombek, вы правы, он не будет намного медленнее. v.insert(x, v.end())будет иметь дополнительную ветвь в начале (из-за перемещения n элементов, но здесь n равно нулю). Однако использование inserter1) передает иное намерение, чем использование push_back2) необычно и заставляет читателя задуматься, 3) является преждевременной пессимизацией.
marton78 02
0

В 2016 году было предложено создать « inserterитератор с одним аргументом ». https://isocpp.org/files/papers/p0471r0.html . Я не мог найти, было ли это предложение. Я думаю, это имеет смысл.

На данный момент у вас может быть такое поведение, определяющее функцию производителя:

template<class Container>
auto sinserter(Container& c){
    using std::end;
    return std::inserter(c, end(c));
}

Используется в качестве:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});
alfC
источник
Это предназначено для работы со всеми стандартными контейнерами? Он не работает с std :: forward_list (ошибка компилятора: «forward_list не имеет члена с именем 'insert'» внутри экземпляра insert_iterator::operator=). Должен ли он?
Дон Хэтч,
@DonHatch, все, что имеет insertend). кажется, что forward_listвообще нет insertоперации, только insert_after. И даже если это изменить, я думаю, его нельзя вставить после конца. Разве вы не можете использовать std::listвместо этого?
alfC
Конечно, мне лично не нужен std :: forward_list. Но меня интересует общее «как мне скопировать один контейнер в другой?» для всех пар контейнеров, для которых это имеет смысл. В настоящее время меня интересуют общие распределители контейнеров, такие как short_alloc @HowardHinnant.
Дон Хэтч,
@DonHatch, дело в том, что на этот вопрос много вариантов. ("" как мне скопировать один контейнер в другой? "). Например, вы хотите сохранить исходные значения, хотите ли вы минимизировать выделение памяти и т. д. В простейшем случае, на мой взгляд, лучшим ответом является использование конструктор контейнер, который принимает два итератора (большинство контейнеров могут это принять) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC
1
Да, std :: set не является SequentialContainer, и это нормально :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) Очень хорошо, я думаю, это мой ответ. Ура!
Дон Хэтч,