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


94

Я думаю, це просте запитання. Мені потрібно зробити щось подібне:

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, а потім просто відсортувати його пізніше. Може, це і краще?

Відповіді:


139

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


2
Оскільки це теж inserter(vec, vec.end())працює для векторів, чому взагалі хтось використовує back_inserter?
NHDaly

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

Це призначено для роботи на всіх стандартних контейнерах? Це не працює на std :: forward_list (помилка компілятора: "forward_list не має учасника з іменем 'insert'", всередині екземпляра insert_iterator::operator=). Чи слід?
Дон Хетч,

@DonHatch, все, що має insertend). здається, що в першу чергу forward_listне проводиться insertоперація insert_after. І навіть якщо це зміниться, я не можу вставити його після закінчення, я думаю. Ви не можете використовувати std::listзамість цього?
alfC

Звичайно, я особисто не потребую std :: forward_list. Але мене цікавить загальне "як скопіювати один контейнер в інший?" для всіх пар контейнерів, для яких це має сенс. Мій поточний інтерес полягає у здійсненні загальних розподільників контейнерів, таких як @HowardHinnant's short_alloc.
Дон Хетч,

@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()) Дуже приємно, я думаю, це моя відповідь. На здоров’я!
Дон Хетч,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.