Iterate over a container with a custom start/end position - c++

I'd like to iterate over a container (say a std::vector) but not from the beginning. I am basically trying to replicate boost::make_iterator_range(v.begin() + 1, v.end()).
I came up with this:
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
std::vector<int> v {1, 2, 3};
std::for_each_n(v.begin() + 1, v.size() - 1, [](auto& n)
{
std::cout << n << '\n';
});
}
However this seems like a poor solution. Also it requires C++17 while I am looking for a solution that works in C++14.
Is there a better way to achieve this without the use of third party libraries?

std::for_each is in C++11 and with that you can do:
std::for_each(v.begin() + 1, v.end(), [](auto &n) { ... });

To replicate make_iterator_range you need an object with a begin and end function. Something like this.
template <typename T>
struct iterator_range {
iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}
T begin() {
return m_begin;
}
T end() {
return m_end;
}
T m_begin, m_end;
};
Then we can make our make_iterator_range function that return an iterator_range.
#include <vector>
#include <iostream>
template <typename T>
struct iterator_range {
iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}
T begin() {
return m_begin;
}
T end() {
return m_end;
}
T m_begin, m_end;
};
template <typename T>
auto make_iterator_range(T begin, T end) {
return iterator_range<T>(begin, end);
}
int main()
{
std::vector<int> v {1, 2, 3};
auto range = iterator_range<decltype(v.begin())>(v.begin() + 1, v.end());
for (auto& i : range) {
std::cout << i << '\n';
}
for (auto& i : make_iterator_range(v.begin() + 1, v.end())) {
std::cout << i << '\n';
}
}

Is there a better way to achieve this ...
A solution using ranges (courtesy of cigien in comments):
for (int element : v | ranges::drop_view(1))
A solution using span
for (int element : span(&v[1], v.size() - 1))
Of course, there aren't std::ranges nor std::span until C++20, so unless you want to use a third party implementation, you'll need to write your own.
If you don't want to implement your own libraries, nor can use C++20, nor want to use a third party library, then std::for_each an alternative that is in C++11.

Related

How to use the range version of `transform()` with two ranges?

The header <algorithm> contains a version of std::transform() taking a two input sequences, an output sequence, and a binary function as parameters, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
std::vector<int> v0{1, 2, 3};
std::vector<int> v1{4, 5, 6};
std::vector<int> result;
std::transform(v0.begin(), v0.end(), v1.begin(), std::back_inserter(result),
[](auto a, auto b){ return a + b; });
std::copy(result.begin(), result.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
C++20 introduced range algoirthms which does include std::ranges::views::transform(R, F) and its implementation std::ranges::views::transform_view. I can see how to use this transform() with one range, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <ranges>
#include <vector>
int main()
{
std::vector<int> v0{1, 2, 3};
for (auto x: std::ranges::views::transform(v0, [](auto a){ return a + 3; })) {
std::cout << x << ' ';
}
std::cout << '\n';
}
However, there is no version supporting more than one range. So, the question becomes: How to use the range version of transform() with two (or more) ranges? On objective of this approach is to benefit from the lazy evaluation of views and avoid the creation of an intermediate sequence (result in the non-ranges version above). A potential use could look like this (putting the function argument in front to make it easier for a potential solution allowing even more than two ranges):
for (auto v: envisioned::transform([](auto a, auto b){ return a + b; }, v0, v1) {
std::cout << v << ' ';
}
std::cout << '\n';
The way you would like to use transform, where you take an arbitrary number of input ranges is not possible directly with what's available in <algorithm> as of C++20. You can of course write such an algorithm yourself without too much effort.
The example with 2 input ranges can be implemented in C++20 like this:
std::ranges::transform(v0, v1,
std::ostream_iterator<int>(std::cout, " "),
std::plus{});
Here's a demo, and this is specifically the last overload of transform listed here.
There is unfortunately no way to write the equivalent version like this:
for (auto v : std::views::transform(v0, v1, std::plus{})) // no, unfortunately
std::cout << v << " ";
but the above implementation does the same thing. It certainly satisfies your requirements of not having to store the results separately; they can be printed as they're generated.
What you're looking for is the algorithm that range-v3 calls zip_with and what we are proposing in P2214 to add to C++23 under the name zip_transform. There is no such algorithm in C++20.
Until then, the range-v3 version is exactly your use-case:
for (auto v : zip_with([](auto a, auto b){ return a + b; }, v0, v1)) {
std::cout << v << ' ';
}
It can handle an arbitrary number of ranges.
Note that there is no piping version here, just as there is not with regular zip.
The answer blow is how I envisioned to answer the question and I think it still contains some interesting bits on how to actually implement a view. It turns out that P2214 mentioned in #Barry's answer has an interesting view (zip_transform) which does an intermediate step of the solution posted below but actually fully covers the functionality needed to do a multi-range transform!
It seems there are essentially two ingredients to using std::ranges::views::transform() with multiple ranges:
Some way to zip the objects at the corresponding positions of the ranges into a std::tuple, probably retaining the value category of the respective values.
Instead of using an n-ary function to take the elements of the range as parameters the function would rather use a std::tuple and possibly use that to call a corresponding n-ary function.
Using this idea would allow creating a version of transform() dealing with an arbitrary number of ranges, although it is easier to take the function object first rather than extract the last element of a parameter pack:
auto transform(auto&& fun, auto&&... ranges)
{
return std::ranges::views::transform(zip(std::forward<decltype(ranges)>(ranges)...),
[fun = std::forward<decltype(fun)>(fun)]
(auto&& t){ return std::apply(fun, std::forward<decltype(t)>(t)); });
}
The zip view used by this implementation can be implemented in terms of std::tuple:
template <typename... Range>
struct zip_view
: std::ranges::view_base
{
template <typename V>
struct rvalue_view
{
std::shared_ptr<std::decay_t<V>> view;
rvalue_view() = default;
rvalue_view(V v): view(new std::decay_t<V>(std::move(v))) {}
auto begin() const { return this->view->begin(); }
auto end() const { return this->view->end(); }
};
template <typename T>
using element_t = std::conditional_t<
std::is_rvalue_reference_v<T>,
rvalue_view<T>,
T
>;
using storage_t = std::tuple<element_t<Range>...>;
using value_type = std::tuple<std::ranges::range_reference_t<std::remove_reference_t<Range>>...>;
using reference = value_type;
using difference_type = std::common_type_t<std::ranges::range_difference_t<Range>...>;
storage_t ranges;
template <typename> struct base;
template <std::size_t... I>
struct base<std::integer_sequence<std::size_t, I...>>
{
using value_type = zip_view::value_type;
using reference = zip_view::value_type;
using pointer = value_type*;
using difference_type = std::common_type_t<std::ranges::range_difference_t<Range>...>;
using iterator_category = std::common_type_t<std::random_access_iterator_tag,
typename std::iterator_traits<std::ranges::iterator_t<Range>>::iterator_category...>;
using iterators_t = std::tuple<std::ranges::iterator_t<Range>...>;
iterators_t iters;
reference operator*() const { return {*std::get<I>(iters)...}; }
reference operator[](difference_type n) const { return {std::get<I>(iters)[n]...}; }
void increment() { (++std::get<I>(iters), ...); }
void decrement() { (--std::get<I>(iters), ...); }
bool equals(base const& other) const {
return ((std::get<I>(iters) == std::get<I>(other.iters)) || ...);
}
void advance(difference_type n){ ((std::get<I>(iters) += n), ...); }
base(): iters() {}
base(const storage_t& s, auto f): iters(f(std::get<I>(s))...) {}
};
struct iterator
: base<std::make_index_sequence<sizeof...(Range)>>
{
using base<std::make_index_sequence<sizeof...(Range)>>::base;
iterator& operator++() { this->increment(); return *this; }
iterator operator++(int) { auto rc(*this); operator++(); return rc; }
iterator& operator--() { this->decrement(); return *this; }
iterator operator--(int) { auto rc(*this); operator--(); return rc; }
iterator& operator+= (difference_type n) { this->advance(n); return *this; }
iterator& operator-= (difference_type n) { this->advance(-n); return *this; }
bool operator== (iterator const& other) const { return this->equals(other); }
auto operator<=> (iterator const& other) const {
return std::get<0>(this->iters) <=> std::get<0>(other.iters);
}
friend iterator operator+ (iterator it, difference_type n) { return it += n; }
friend iterator operator+ (difference_type n, iterator it) { return it += n; }
friend iterator operator- (iterator it, difference_type n) { return it -= n; }
friend difference_type operator- (iterator it0, iterator it1) {
return std::get<0>(it0.iters) - std::get<0>(it1.iters);
}
};
zip_view(): ranges() {}
template <typename... R>
zip_view(R&&... ranges): ranges(std::forward<R>(ranges)...) {}
iterator begin() const { return iterator(ranges, [](auto& r){ return std::ranges::begin(r); }); }
iterator end() const { return iterator(ranges, [](auto& r){ return std::ranges::end(r); }); }
};
auto zip(auto&&... ranges)
-> zip_view<decltype(ranges)...>
{
return {std::forward<decltype(ranges)>(ranges)...};
}
This implementation makes some decisions about the value_type and the reference type and how to keep track of the different ranges. Other choices may be more reasonable (P2214 makes slightly different, probably better, choices). The only tricky bit in this implementation is operating on the std::tuples which requires a parameter pack containing indices or a suitable set of algorithms on std::tuples.
With all of that in place a multi-range transform can be used nicely, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <memory>
#include <ranges>
#include <utility>
#include <tuple>
#include <type_traits>
#include <vector>
// zip_view, zip, and transform as above
int main()
{
std::vector<int> v0{1, 2, 3};
std::vector<int> v1{4, 5, 6};
std::vector<int> v2{7, 8, 9};
for (auto x: transform([](auto a, auto b, auto c){ return a + b + c; }, v0, v1, v2)) {
std::cout << x << ' ';
}
std::cout << '\n';
}

What STL algorithm can determine if exactly one item in a container satisfies a predicate?

I need an STL algorithm that takes a predicate and a collection and returns true if one and only one member of the collection satisfies the predicate, otherwise returns false.
How would I do this using STL algorithms?
E.g., to replace the following with STL algorithm code to express the same return value.
int count = 0;
for( auto itr = c.begin(); itr != c.end(); ++itr ) {
if ( predicate( *itr ) ) {
if ( ++count > 1 ) {
break;
}
}
}
return 1 == count;
Two things come to my mind:
std::count_if and then compare the result to 1.
To avoid traversing the whole container in case eg the first two elements already match the predicate I would use two calls looking for matching elements. Something along the line of
auto it = std::find_if(begin,end,predicate);
if (it == end) return false;
++it;
return std::none_of(it,end,predicate);
Or if you prefer it more compact:
auto it = std::find_if(begin,end,predicate);
return (it != end) && std::none_of(std::next(it),end,predicate);
Credits goes to Remy Lebeau for compacting, Deduplicator for debracketing and Blastfurnance for realizing that we can also use none_of the std algorithms.
You can use std::count_if† to count and return if it is one.
For example:
#include <iostream>
#include <algorithm> // std::count_if
#include <vector> // std::vector
#include <ios> // std::boolalpha
template<class Iterator, class UnaryPredicate>
constexpr bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
return std::count_if(begin, end, pred) == 1;
}
int main()
{
std::vector<int> vec{ 2, 4, 3 };
// true: if only one Odd element present in the container
std::cout << std::boolalpha
<< is_count_one(vec.cbegin(), vec.cend(),
[](const int ele) constexpr noexcept -> bool { return ele & 1; });
return 0;
}
†Update: However, std::count_if counts entire element in the container, which is not good as the algorithm given in the question. The best approach using the standard algorithm collections has been mentioned in #formerlyknownas_463035818 's answer.
That being said, OP's approach is also good as the above mentioned best standard approach, where a short-circuiting happens when count reaches 2. If someone is interested in a non-standard algorithm template function for OP's approach, here is it.
#include <iostream>
#include <vector> // std::vector
#include <ios> // std::boolalpha
#include <iterator> // std::iterator_traits
template<class Iterator, class UnaryPredicate>
bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
typename std::iterator_traits<Iterator>::difference_type count{ 0 };
for (; begin != end; ++begin) {
if (pred(*begin) && ++count > 1) return false;
}
return count == 1;
}
int main()
{
std::vector<int> vec{ 2, 3, 4, 2 };
// true: if only one Odd element present in the container
std::cout << std::boolalpha
<< is_count_one(vec.cbegin(), vec.cend(),
[](const int ele) constexpr noexcept -> bool { return ele & 1; });
return 0;
}
Now that can be generalized, by providing one more parameter, the number of N element(s) has/ have to be found in the container.
template<typename Iterator>
using diff_type = typename std::iterator_traits<Iterator>::difference_type;
template<class Iterator, class UnaryPredicate>
bool has_exactly_n(Iterator begin, const Iterator end, UnaryPredicate pred, diff_type<Iterator> N = 1)
{
diff_type<Iterator> count{ 0 };
for (; begin != end; ++begin) {
if (pred(*begin) && ++count > N) return false;
}
return count == N;
}
Starting from formerlyknownas_463035818's answer, this can be generalized to seeing if a container has exactly n items that satisfy a predicate. Why? Because this is C++ and we're not satisfied until we can read email at compile time.
template<typename Iterator, typename Predicate>
bool has_exactly_n(Iterator begin, Iterator end, size_t count, Predicate predicate)
{
if(count == 0)
{
return std::none_of(begin, end, predicate);
}
else
{
auto iter = std::find_if(begin, end, predicate);
return (iter != end) && has_exactly_n(std::next(iter), end, count - 1, predicate);
}
}
Using std::not_fn to negate a predicate
As the core of the algorithm of this question (as has been elegantly covered by combining std::find_if and std::none_of in the accepted answer), with short-circuiting upon failure, is to scan a container for a unary predicate and, when met, continue scanning the rest of the container for the negation of the predicate, I will mention also the negator std::not_fn introduced in C++17, replacing the less useful std::not1 and std::not2 constructs.
We may use std::not_fn to implement the same predicate logic as the accepted answer (std::find_if conditionally followed by std::none_of), but with somewhat different semantics, replacing the latter step (std::none_of) with std::all_of over the negation of the unary predicate used in the first step (std::find_if). E.g.:
// C++17
#include <algorithm> // std::find_if
#include <functional> // std::not_fn
#include <ios> // std::boolalpha
#include <iostream>
#include <iterator> // std::next
#include <vector>
template <class InputIt, class UnaryPredicate>
constexpr bool one_of(InputIt first, InputIt last, UnaryPredicate p) {
auto it = std::find_if(first, last, p);
return (it != last) && std::all_of(std::next(it), last, std::not_fn(p));
}
int main() {
const std::vector<int> v{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(v.begin(), v.end(), [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
A parameter pack approach for static size containers
As I’ve already limited this answer to C++14 (and beyond), I’ll include an alternative approach for static size containers (here applied for std::array, specifically), making use of std::index_sequence combined with parameter pack expansion:
#include <array>
#include <ios> // std::boolalpha
#include <iostream>
#include <utility> // std::(make_)index_sequence
namespace detail {
template <typename Array, typename UnaryPredicate, std::size_t... I>
bool one_of_impl(const Array& arr, const UnaryPredicate& p,
std::index_sequence<I...>) {
bool found = false;
auto keep_searching = [&](const int n){
const bool p_res = found != p(n);
found = found || p_res;
return !found || p_res;
};
return (keep_searching(arr[I]) && ...) && found;
}
} // namespace detail
template <typename T, typename UnaryPredicate, std::size_t N,
typename Indices = std::make_index_sequence<N>>
auto one_of(const std::array<T, N>& arr,
const UnaryPredicate& p) {
return detail::one_of_impl(arr, p, Indices{});
}
int main() {
const std::array<int, 5> a{1, 3, 5, 6, 7};
std::cout << std::boolalpha << "Exactly one even number : "
<< one_of(a, [](const int n) {
return n % 2 == 0;
}); // Exactly one even number : true
}
This will also short-circuit upon early failure (“found more than one”), but will contain a few more simple boolean comparisons than in the approach above.
However, note that this approach could have its draw-backs, particularly for optimized code for container inputs with many elements, as is pointed out by #PeterCordes in a comment below. Citing the comment (as comments are not guaranteed to persist over time):
Just because the size is static doesn't mean that fully unrolling the loop with templates is a good idea. In the resulting asm, this needs a branch every iteration anyway to stop on found, so that might as well be a loop-branch. CPUs are good at running loops (code caches, loopback buffers). Compilers will fully unroll static-sized loops based on heuristics, but probably won't roll this back up if a is huge. So your first one_of implementation has the best of both worlds already, assuming a normal modern compiler like gcc or clang, or maybe MSVC

Merge vector of vectors into a single vector

I have vector of vectors of T:
std::vector<std::vector<T>> vector_of_vectors_of_T;
I want to merge all of them into single vector of T:
std::vector<T> vector_of_T;
I am currently using this method:
size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
total_size += items.size();
}
vector_of_T.reserve(total_size);
for (auto const& items: vector_of_vectors_of_T){
vector_of_T.insert(end(vector_of_T), begin(items), end(items));
}
Is there more straightforward method? Like a ready std function? If not, is there more efficient way to do it manually?
It's a nice exercise to try and write up a generic join. The code below takes a nested container R1<R2<T> and returns a joined container R1<T>. Note that because of the allocator parameters in the Standard Library, this is a bit cumbersome. No attempt is being made to check for allocator compatibility etc.
Fortunately, there's action::join function in the upcoming range-v3 library by Eric Niebler, that is quite robust already and works today on Clang:
#include <range/v3/all.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
// quick prototype
template<template<class, class...> class R1, template<class, class...> class R2, class T, class... A1, class... A2>
auto join(R1<R2<T, A2...>, A1...> const& outer)
{
R1<T, A2...> joined;
joined.reserve(std::accumulate(outer.begin(), outer.end(), std::size_t{}, [](auto size, auto const& inner) {
return size + inner.size();
}));
for (auto const& inner : outer)
joined.insert(joined.end(), inner.begin(), inner.end());
return joined;
}
int main()
{
std::vector<std::vector<int>> v = { { 1, 2 }, { 3, 4 } };
// quick prototype
std::vector<int> w = join(v);
std::copy(w.begin(), w.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
// Eric Niebler's range-v3
std::vector<int> u = ranges::action::join(v);
std::copy(u.begin(), u.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
}
Live Example
Using back_inserter and move;
size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
total_size += items.size();
}
vector_of_T.reserve(total_size);
for (auto& items: vector_of_vectors_of_T){
std::move(items.begin(), items.end(), std::back_inserter(vector_of_T));
}
Instead of copying, std::move gives it a bit performance enhancement.
I guess you could try std::merge/std::move used in a loop - that are already existing std algorithms. Don't know if that's faster.

c++ stl convolution

Is there a nice implementation of the algorithm to calculate the convolution of two ranges in C++ STL (or even boost)?
i.e. something with prototype (convolution of two ranges a..b and c..d):
template< class Iterator >
void convolution(Iterator a, Iterator b, Iterator c, Iterator d);
which modifies a..b range
Yes std::transform
std::transform(a, b, c, a, Op);
// a b is the the first input range
// c is the start of the second range (which must be at least as large as (b-a)
//
// We then use a as the output iterator as well.
// Op is a BinaryFunction
To answer the comment on how to perform accumulation of state in the comments:
struct Operator
{
State& state;
Operator(Sate& state) : state(state) {}
Type operator()(TypeR1 const& r1Value, TypeR2 const& r2Value) const
{
Plop(state, r1Value, r2Value);
return Convolute(state, r2Value, r2Value);
}
};
State theState = 0;
Operator Op(theState);
I'm not quite sure what a "convolution" from two sequences to one of these two sequences is supposed to be: It seems to be a different understanding than my understanding. Below is a version of convolution using a variable number of iterators. Because I'm actually just too lazy for now, I'll use a somewhat uncommon notion of passing the destination iterator as first argument rather than as last argument. Here is an implementation of a corresponding zip() algorithms:
#include <tuple>
namespace algo
{
template <typename... T>
void dummy(T...)
{
}
template <typename To, typename InIt, typename... It>
To zip(To to, InIt it, InIt end, It... its)
{
for (; it != end; ++it, ++to) {
*to = std::make_tuple(*it, *its...);
algo::dummy(++its...);
}
return to;
}
}
Below is a simple test program I used to verify that the above does what I intended it to do:
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>
enum class e { a = 'a', b = 'b', c = 'c' };
std::ostream& operator<< (std::ostream& out,
std::tuple<int, double, e> const& v)
{
return out << "["
<< std::get<0>(v) << ", "
<< std::get<1>(v) << ", "
<< char(std::get<2>(v)) << "]";
}
int main()
{
typedef std::tuple<int, double, e> tuple;
std::vector<int> v{ 1, 2, 3 };
std::deque<double> d{ 1.1, 2.2, 3.3 };
std::list<e> l{ e::a, e::b, e::c };
std::vector<tuple> r;
algo::zip(std::back_inserter(r), v.begin(), v.end(), d.begin(), l.begin());
std::copy(r.begin(), r.end(),
std::ostream_iterator<tuple>(std::cout, "\n"));
}

Why was pair range access removed from C++11?

I just discovered that at one point, the C++11 draft had std::begin/std::end overloads for std::pair that allowed treating a pair of iterators as a range suitable for use in a range-based for loop (N3126, section 20.3.5.5), but this has since been removed.
Does anyone know why it was removed?
I find the removal very unfortunate, because it seems there is no other way to treat a pair of iterators as a range. Indeed:
The lookup rules for begin/end in a range-based for loop say that begin/end are looked for in 1) as member functions of the range object 2) as free functions in "associated namespaces"
std::pair does not have begin/end member functions
The only associated namespace for std::pair<T, U> in general is namespace std
We are not allowed to overload std::begin/std::end for std::pair ourselves
We cannot specialize std::begin/std::end for std::pair (because the specialization would have to be partial and that's not allowed for functions)
Is there some other way that I am missing?
I think the 2009 paper "Pairs do not make good ranges" by Alisdair Meredith is at least part of the answer. Basically, many algorithms return pairs of iterators that are actually not guaranteed to be valid ranges. It seems they removed the support for pair<iterator,iterator> from the for-range loop for this reason. However, the proposed solution has not been fully adopted.
If you know for certain that some pair of iterators really represents a valid range then you could wrap them into a custom type which offers begin()/end() member functions:
template<class Iter>
struct iter_pair_range : std::pair<Iter,Iter> {
iter_pair_range(std::pair<Iter,Iter> const& x)
: std::pair<Iter,Iter>(x)
{}
Iter begin() const {return this->first;}
Iter end() const {return this->second;}
};
template<class Iter>
inline iter_pair_range<Iter> as_range(std::pair<Iter,Iter> const& x)
{ return iter_pair_range<Iter>(x); }
int main() {
multimap<int,int> mm;
...
for (auto& p : as_range(mm.equal_range(42))) {
...
}
}
(untested)
I agree this is a bit of a wart. Functions which return valid ranges (like equal_range) should say so using an appropriate return type. It's a bit embarrasing that we have to manually confirm this via something like as_range above.
You can use boost::make_iterator_range.
It constructs an iterator_range with begin() and end() methods.
boost::make_iterator_range can accept std::pair of iterators.
expanding on the above answer using c++11 optimisations:
#include <utility>
template<class Iter>
struct range_t : public std::pair<Iter, Iter> {
using pair_t = std::pair<Iter, Iter>;
range_t(pair_t&& src)
: std::pair<Iter, Iter>(std::forward<pair_t>(src))
{}
using std::pair<Iter, Iter>::first;
using std::pair<Iter, Iter>::second;
Iter begin() const { return first; }
Iter end() const { return second; }
};
template<class Iter>
range_t<Iter> range(std::pair<Iter, Iter> p) {
return range_t<Iter>(std::move(p));
}
template<class Iter>
range_t<Iter> range(Iter i1, Iter i2) {
return range_t<Iter>(std::make_pair(std::move(i1), std::move(i2)));
}
// TEST:
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> mySet { 6,4,5,5,5,3,3,67,8,89,7,5,45,4,3 };
cout << "similar elements: ";
for (const auto&i : range(mySet.lower_bound(5), mySet.upper_bound(10))) {
cout << i << ",";
}
cout << "\n";
int count = 0, sum = 0;
for (const auto& i: range(mySet.equal_range(5)))
{
++count;
sum += i;
}
cout << "5 appears " << count << " times\n"
<< "the sum is " << sum << "\n";
return 0;
}