Reputation: 28490
Range-v3 has ranges::views::drop
and ranges::views::drop_last
to remove elements from the front or the back of a view.
Does it offer a similar functions to prepend/append elements to a view?
For now, the shortest way I've found is to concat
the range/container with a iota
or with a single
:
#include <assert.h>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/to_container.hpp>
using namespace ranges;
using namespace views;
int main() {
std::vector<int> v{1,2,3};
auto wi = concat(iota(0,1),v);
assert(((wi | to_vector) == std::vector<int>{0,1,2,3}));
auto ws = concat(single(0), v);
assert(((ws | to_vector) == std::vector<int>{0,1,2,3}));
}
Upvotes: 14
Views: 982
Reputation: 372
Prepending or appending elements to a view differs from using drop
or drop_last
in that we must manage the lifetime of the elements to be prepended or appended. There are at least two options. In the straightforward scenario, we could store these elements in a separate container, outside the view adaptors, and utilize concat
to achieve the desired outcome. Conversely, if we aim to store the elements in the view adaptors themselves, we must ensure all copies of them share these elements. A feasible (though not optimal) implementation of the prepend_view
class, which combines std::shared_ptr
, std::vector
, and concat_view
, is as follows:
template <typename Rng, typename T>
requires viewable_range<Rng> && input_range<Rng>
struct prepend_view : concat_view<all_t<std::vector<T> &>, Rng> {
private:
using vector_t = std::vector<T>;
using concat_view_t = concat_view<all_t<vector_t &>, Rng>;
std::shared_ptr<vector_t> ptr;
public:
constexpr prepend_view() = default;
constexpr prepend_view(Rng rng, std::initializer_list<T> list)
requires copy_constructible<T>
: concat_view_t(), ptr(std::make_shared<vector_t>(list)) {
static_cast<concat_view_t &>(*this) = concat_view_t(*ptr, std::move(rng));
}
};
template <typename Rng, typename T>
prepend_view(Rng &&, std::initializer_list<T>) -> prepend_view<all_t<Rng>, T>;
Additionally, the function object prepend
:
struct prepend_base_fn {
template <typename Rng, copy_constructible T>
requires viewable_range<Rng> && input_range<Rng>
constexpr auto operator()(Rng &&rng, std::initializer_list<T> list) const {
return prepend_view(rng, list);
}
};
struct prepend_fn : prepend_base_fn {
using prepend_base_fn::operator();
template <copy_constructible T>
constexpr auto operator()(std::initializer_list<T> list) const {
return make_view_closure(bind_back(prepend_base_fn(), list));
}
};
RANGES_INLINE_VARIABLE(prepend_fn, prepend)
The test code [Godbolt]:
int main() {
int a[] = {1, 2, 3, 4, 5, 6};
auto v = std::vector{3, 2, 1};
assert(equal(a, v | prepend({6, 5, 4}) | reverse));
}
append_view
and append
could be implemented following similar patterns.
Upvotes: 1