std:: insert итератор для неупорядоченных множеств (или карт)?
Есть ли итератор вставки в std::
для неупорядоченных множеств? Насколько я вижу, std::inserter
требует аргумент итератора. Это небезопасно для неупорядоченных контейнеров (по крайней мере, для boost::unordered_set
), потому что они могут перераспределить во время insert
операция и рендеринг пройденного .begin()
неверный итератор.
Так что в настоящее время я должен пройти свой собственный итератор, который по сути является boost::function_output_iterator
с функтором, который просто вызывает unorderedSet.insert(param1)
,
Почему это так std::inserter
даже требует hint
аргумент итератора в любом случае?
0 ответов
Нет. Причина, по которойhint
требуется аргумент, что std::inserter
предназначен для контейнеров, в которых требуется позиция в контексте вставки. Как вы знаете, это не относится к неупорядоченным контейнерам.
vector
в примере контейнера, где знание position
это требование для вставки. Из cppreference:
(1) iterator insert( iterator pos, const T& value ); // (until C++11) iterator insert( const_iterator pos, const T& value ); // (since C++11)
(2) iterator insert( const_iterator pos, T&& value ); // (since C++11)
(3) void insert( iterator pos, size_type count, const T& value ); // (until C++11) iterator insert( const_iterator pos, size_type count, const T& value ); // (since C++11)
(4) template< class InputIt > void insert( iterator pos, InputIt first, InputIt last); // (until C++11) template< class InputIt > iterator insert( const_iterator pos, InputIt first, InputIt last ); // (since C++11)
(5) iterator insert( const_iterator pos, std::initializer_list<T> ilist ); // (since C++11)
Вставляет элементы в указанное место в контейнере.
1-2) вставляет значение перед поз.
3) вставляет количество копий значения перед поз.
4) вставляет элементы из диапазона [первый, последний) перед поз.
Эта перегрузка имеет тот же эффект, что и перегрузка (3), если InputIt является интегральным типом. (до C++11)
Эта перегрузка участвует в разрешении перегрузки, только если InputIt квалифицируется как LegacyInputIterator, чтобы избежать неоднозначности с перегрузкой (3). (начиная с C++11) Поведение не определено, если first и last являются итераторами в * this.
5) вставляет элементы из списка инициализаторов ilist перед поз.
Я знаю, что это не тот ответ, который вы ищете, но развернуть свой собственный достаточно просто, если не сказать многословно:
template<typename Container>
class unordered_inserter {
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using reference_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
using container_type = Container;
unordered_inserter& operator++() {return *this;} //no-op
unordered_inserter& operator++(int) {return *this;} //no-op
unordered_inserter& operator*() {return *this;} //no-op
constexpr unordered_inserter& operator=(const typename Container::value_type& value) {
container->insert(value);
return *this;
}
constexpr unordered_inserter& operator=(typename Container::value_type&& value) {
container->insert(std::move(value));
return *this;
}
unordered_inserter(Container* container)
: container(container)
{}
protected:
Container* container;
};
Вероятно, это можно будет отредактировать для поддержки других видов вставки, но я думаю, что пока этого достаточно.
Вот я немного поиграю с этим:
int main() {
std::unordered_map<int, int> m;
std::istringstream iss("1 2 3 4 5 6");
std::transform(std::istream_iterator<int>(iss), std::istream_iterator<int>(), unordered_inserter(&m), [](int v) {
return decltype(m)::value_type{v, v*v};
});
std::transform(m.begin(), m.end(), std::ostream_iterator<std::string>(std::cout, "\n"), [](auto pair) {
return std::to_string(pair.first) + "," + std::to_string(pair.second);
});
}
Живи на Godbolt
Здесь следует отметить, что ваше утверждение о том, что hint
аргумент для неупорядоченных контейнеров небезопасен, неверен. когдаoperator=
вызывается, и новый элемент вставляется в контейнер, iter
член обновлен до любого insert
возвращается. Поскольку это значение должно быть действительным, нет возможности, чтобыiter
когда-либо может быть признан недействительным.