Creating a Generic Insertion Iterator, Part 2 – Raymond Chen
Last time, our generic insertion iterator failed to meet the requirements of default constructibility and assignability because it stored the lambda directly. To fix this, we now store a pointer to the lambda instead, ensuring the iterator meets standard requirements while still allowing flexible insertion logic.
Creating a Generic Insertion Iterator, Part 2
by Raymond Chen
From the article:
Last time, we tried to create a generic insertion iterator but ran into trouble because our iterator failed to satisfy the iterator requirements of default constructibility and assignability.
We ran into this problem because we stored the lambda as a member of the iterator.
So let’s not do that!
Instead of saving the lambda, we’ll just save a pointer to the lambda.
- template<typename Lambda>
- struct generic_output_iterator
- {
- using iterator_category = std::output_iterator_tag;
- using value_type = void;
- using pointer = void;
- using reference = void;
- using difference_type = void;
- generic_output_iterator(Lambda&& lambda) noexcept :
- insert(std::addressof(lambda)) {}
- generic_output_iterator& operator*() noexcept
- { return *this; }
- generic_output_iterator& operator++() noexcept
- { return *this; }
- generic_output_iterator& operator++(int) noexcept
- { return *this; }
- template<typename Value>
- generic_output_iterator& operator=(
- Value&& value)
- {
- (*insert)(std::forward<Value>(value));
- return *this;
- }
- protected:
- Lambda* insert;
- };
- template<typename Lambda>
- generic_output_iterator<Lambda>
- generic_output_inserter(Lambda&& lambda) noexcept {
- return generic_output_iterator<Lambda>(
- std::forward<Lambda>(lambda));
- }
- template<typename Lambda>
- generic_output_iterator(Lambda&&) ->
- generic_output_iterator<Lambda>;
This requires that the lambda remain valid for the lifetime of the iterator, but that may not a significant burden. Other iterators also retain references that are expected to remain valid for the lifetime of the iterator. For example,
std::back_inserter(v)
requires thatv
remain valid for as long as you use the inserter. And if you use the iterator immediately, then the requirement will be satisfied: