C++ map erase using forward and reverse iterators - c++

I have a template function like this
template<typename T>
void foo(T start , T end)
{
while(start != end)
{
if(cond)
m.erase(start);
start++;
}
}
Now I have to pass both forward and reverse iterator as the typename. Two separate calls in which one is forward and one is reverse iterator. How do I do this ?

First of all, let me reiterate LogicStuff's comment: You should really try to pass in compatible iterators instead.
If you really, really, really have no alternative to doing it the way you are doing it right now, you could use some template functions:
#include <vector>
#include <iostream>
// Used when both iterators have the same type
template <typename T>
void foo(T begin, T end)
{
for (; begin != end; ++begin)
{
std::cout << " " << *begin;
}
}
// Overload for a forward begin and reverse end
template <typename T>
void foo(T begin, std::reverse_iterator<T> end)
{
foo(begin, end.base());
}
// Overload for a reverse begin and forward end
template <typename T>
void foo(std::reverse_iterator<T> begin, T end)
{
foo(begin, std::reverse_iterator<T>(end));
}
int main()
{
std::vector<int> v { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foo(v.begin(), v.end()); std::cout << std::endl;
foo(v.begin(), v.rbegin()); std::cout << std::endl;
foo(v.rbegin(), v.begin()); std::cout << std::endl;
foo(v.rbegin(), v.rend()); std::cout << std::endl;
}
See it running on ideone.
Here I convert reverse iterators to forward iterators. This SO post gives you more details about that. But read that post very carefully, there be dragons. My example above just outputs numbers and does not modify the underlying container. And I do not check the validity of the iterators, nor do I do any bounds checking. For your own case, make sure you test all the edge cases (either iterator being at or beyond the beginning/end of your container; off-by-one errors, etc.).
Also, note that in your example code, the call to erase() invalidates the iterator, so you should write the loop body like this:
if (cond) {
// guarantees to return an iterator to the element following
// the erased element.
start = m.erase(start);
} else {
++start;
}
Edit: If you require that iterators are always converted to their forward equivalents, you can change the last overload and add another:
template <typename T>
void foo(std::reverse_iterator<T> begin, T end)
{
foo(end, begin.base()); // Note: order of iteration reversed!
}
template <typename T>
void foo(std::reverse_iterator<T> begin, std::reverse_iterator<T> end)
{
foo(end.base(), begin.base()); // Note: order of iteration reversed!
}
But be aware that the order of iteration is now reversed: in my example, calling foo(v.rbegin(), v.rend()) printed 9 8 7 ... 1 in the first incarnation, and now it prints 1 2 3 ... 9. Example here.
And again, you'd be off much better if you could feed in compatible iterators instead.

Related

C++ Function that accepts array.begin() and array.end() as arguments

I want my function to be able to take array.begin() and array.end() as arguments. As far as I understand, the begin/end functions return a pointer to the first/last element of the array. Then why does the following code not work? How should I write the code instead?
#include <iostream>
#include <array>
template<class T> void foo(const T* begin, const T* end) {
...
}
int main() {
std::array<int, 5> arr = { 1, 2, 3, 4, 5 };
foo(arr.begin(), arr.end()); // <-- error!
return 0;
}
As far as I understand, the begin/end functions return a pointer to the first/last element of the array
No. begin and end return iterators. The standard library works with iterators. Iterators are a generalization of pointers.
Iterators behave like pointers and you use them like pointers (e.g. *it to access the element), with some caveats: not all iterators have all the operations a pointer does. A pointer satisfies the random access iterator concept, so on some implementations the iterator of std::array could be just an alias for the pointer type, but you can't rely on that. E.g. on the same compiler it can be a pointer for the release build, but a full class for the debug build.
The idiomatic way is to write:
template<class It>
void foo(It begin, It end) {
for (auto it = begin; it != end; ++it) {
const auto& elem = *it;
// ..
}
}
Since C++20 we should transition from iterator pairs to ranges:
void foo(std::ranges::range const auto& r) {
for (const auto& elem : r) {
// ...
}
}
As far as I understand, the begin/end functions return a pointer
Your understanding is (generally) wrong. begin and end functions return std::array::iterator, as per documentation. That type is not necessarily a pointer.
to the first/last element of the array
end isn't an iterator to the last element, but one past the last element.
how to solve my problem the right way?
Accept arguments of type std::array::iterator.
That said, given that foo is a template, there seems to be little reason to not allow the argument to be of any iterator type:
template<class It>
void
foo(It begin, It end)
Alternatively, you could accept a range:
template<class Range>
void
foo(const Range& range)
// usage
foo(arr);

boost::transform_iterator and std::iter_swap

I am trying to generalize a function I have which used to take two iterators to a vector of a specific data-structure, and re-arrange the elements in a certain way using std::iter_swap (like std::sort does).
Since this function only actually needs a subset of the data, and I will need to use it in other contexts in the future, I thought about removing the dependency on the data structure, and use boost::transform_iterator at the point of call to handle the transformation.
Unfortunately, it seems that boost::transform_iterator is not happy with this change. I can imagine why: std::iter_swap is usually implemented as std::swap(*lhs, *rhs), and dereferencing the transform_iterator does not yield the original element to swap in the correct way.
I was wondering if there was a way to handle this case. I am open to use boost::range or the experimental std::ranges ts if it needed.
This question is probably similar to this one, but even there the solution ends up modifying the subset of data the algorithm needs, rather than the outside structure.
Here is an MWE:
#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
struct A {
int x;
int y;
};
template <typename It>
void my_invert(It begin, It end) {
while (begin < end) {
std::iter_swap(begin++, --end);
}
}
template <typename It>
void my_print(It begin, It end) {
for (; begin != end; ++begin)
std::cout << (*begin) << ' ';
std::cout << '\n';
}
int main() {
std::vector<int> x{7,6,5,4,3,2};
my_invert(std::begin(x), std::end(x));
my_print(std::begin(x), std::end(x));
auto unwrap = +[](const A & a) { return a.x; };
std::vector<A> y{{9,8}, {7,6}, {5,4}, {3,2}};
auto begin = boost::make_transform_iterator(std::begin(y), unwrap);
auto end = boost::make_transform_iterator(std::end(y), unwrap);
//my_invert(begin, end); // Does not work.
my_print(begin, end);
return 0;
}
Accessing the underlying iterator
You could access the base() property of transform_iterator (inherited publicly from iterator_adaptor) to implement your custom transform_iter_swap, for swapping the underlying data of the wrapped iterator.
E.g.:
template<class IteratorAdaptor>
void transform_iter_swap(IteratorAdaptor a, IteratorAdaptor b)
{
std::swap(*a.base(), *b.base());
}
template <typename It>
void my_invert(It begin, It end) {
while (begin < end) {
transform_iter_swap(begin++, --end);
}
}
After which your example (omitting the std::vector part) runs as expected:
my_invert(begin, end); // OK
my_print(begin, end); // 3 5 7 9
If you want a general function template to cover both the boost (adaptor) iterators as well as typical iterators, you could e.g. use if constexpr (C++17) based on whether the iterators public typedef iterator_category derives from boost::iterators::no_traversal_tag or not:
// expand includes with
#include <boost/iterator/iterator_categories.hpp>
template <class It>
void iter_swap(It a, It b) {
if constexpr(std::is_base_of<
boost::iterators::no_traversal_tag,
typename It::iterator_category>::value) {
std::swap(*a.base(), *b.base());
}
else {
std::swap(*a, *b);
}
}
template <typename It>
void my_invert(It begin, It end) {
while (begin < end) {
iter_swap(begin++, --end);
}
}
The problem comes from the unary predicate you've passed. Notice, that since you allow the return type to be deduced, the return type is deduced to be an int, a copy is returned, and the compilation fails when you try to swap two unmodifiable ints. However, if you were to specify the return type to be int&, like so:
auto unwrap = [](A & a)->int& { return a.x; }; // explicitly demand to return ref
It will compile, and reverse the elements. Tested on gcc 8.1.0 and clang 6.0.0.

Declare a template function which receives two generic iterator as parameters

I need to create a function which receives the iterator from the begin and the end of one container. Then it should show the content in the console.
My problem is that i dont know how to declare the iterator so that it can work with any type of container
This is what I did:
template <class T>
void print(typename iterator<T> &beg, typename iterator<T> &end) {
while (beg != end) {
cout << *beg << endl;
beg++;
}
}
The std::iterator class is really just a convenience; there's nothing in the standard that requires all iterators to inherit from it. Additionally, std::iterator doesn't have virtual methods, so it's not nearly the same thing as taking an Iterator<T> in, say, Java, where invoking the next() method would call the appropriate next(). You want to take a general type T, not just an std::iterator, so that the compiler will resolve to the correct overloads of operator++ and operator* at compile-time.
template <typename T>
void print(T iter, const T& end) {
// Taking the first argument by value ensures that
// we don't modify the caller's variables
while (iter != end) {
cout << *iter << endl;
++iter;
}
}
This will work for any forward iterators, which is what you're dealing with 99% of the time.
I need to create a function which receives the iterator from the begin
and the end of one container.
Look how standard functions do it, for example std::find:
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
Observations:
InputIt does not need to inherit from the (now obsolete) std::iterator class or any other class. Among other advantages, this allows the function to be used with an array.
The same iterator type is used for start and end.
The iterators are passed by value.
The template parameter does not specify the iterators' value type.
Just do it exactly like that in your own code and you'll be fine:
#include <iostream>
#include <vector>
template <class Iterator> // not T
void print(Iterator beg, Iterator end) {
while (beg != end) {
std::cout << *beg << '\n';
beg++;
}
}
int main() {
std::vector<int> const vec = { 1, 2, 3 };
int const array[] = { 1, 2, 3 };
using std::begin;
using std::end;
print(begin(vec), end(vec));
print(begin(array), end(array));
}

c++ proper syntax for templated function of `std::vector<T>::iterator` [duplicate]

This question already has an answer here:
C++ template won't accept iterators
(1 answer)
Closed 9 years ago.
I'm trying to code a template that takes iterators to any type of vector as its arguments. When I try to compile the following, it gives me a no matching function call error.
#include <vector>
struct A { int x; };
template <class T>
void process (typename std::vector<T>::iterator begin,
typename std::vector<T>::iterator end)
{ for(; begin != end; begin++) { /*do_something*/ } }
int main()
{
std::vector <A> obj;
process(obj.begin(), obj.end());
}
1 the type T cannot be deduced from the argument types.
2 Why would you want to restrict the function to only accept iterators to std::vector elements? If you really only want vector elements, better take std::vector<T> const& as argument. But better simply take any iterator arguments (or any container argument).
edit okay, here is an example. You may omit the static_assert, when this becomes identical (apart from the return type) to std::for_each(begin,end,do_something);
template <class It>
void process(It begin, const It end)
{
static_assert(std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
"arguments not random access iterators");
for (; begin != end; ++begin)
do_something(*begin);
}
Per the OP's request, see below. you can use any valid container forward iterator that supports value references from operator *() I.e. a vector, deque, list, etc. This does not employ the static assert logic mentioned by chris, I leave that for you to decide.
#include <iostream>
#include <iterator>
template<typename Iterator>
void process(Iterator start, Iterator stop)
{
typedef typename std::iterator_traits<Iterator>::value_type value_type;
for (Iterator it=start; it != stop; ++it)
{
const value_type& val = (*it);
// do something with val
std::cout << val << std::endl;
}
}
int main()
{
int ar[] = { 1,2,3,4,5 };
process(std::begin(ar), std::end(ar));
return 0;
}

Reverse iterate over a std::vector with forward iterators

If I have a function which takes a std::vector<T>::const_iterator called begin and a std::vector<T>::const_iterator called end, is it possible for me to iterate over it in a backwards direction?
UPDATE
I can't change the function signature, which is like this:
void func(Foo::const_iterator begin, Foo::const_iterator end)
{
...
}
and called with:
func(foo.begin(), foo.end());
which I also can't change
Yes, you can.
template <typename Foo>
void test(typename Foo::const_iterator begin,
typename Foo::const_iterator end)
{
std::reverse_iterator<typename Foo::const_iterator>
rbegin(end),
rend(begin);
std::copy(rbegin, rend, std::ostream_iterator<typename std::iterator_traits<typename Foo::const_iterator>::value_type>(std::cout));
}
int main()
{
std::vector<int> v{3,1,4,1,5,9,2,6};
test<std::vector<int> >(v.begin(), v.end());
}
I may have misunderstood the question, but do you just need:
while (begin != end) {
--end;
// do something with *end
}
If you need an iterator that goes backwards, ipc's answer gives you one.
In practice with vector you could probably get away with while (begin != end--), but don't be tempted. It's undefined behavior to decrement an iterator in the case where it's at the start of the vector (i.e. when it's equal to the result of vector::begin()).
This code requires at least a BidirectionalIterator. Fortunately, vector has a RandomAccessIterator, which is even better.
If you genuinely only had a ForwardIterator, then you'd have to iterate over the range and store the iterator values somewhere (like a stack), and then use them in reverse order:
std::stack<Foo::const_iterator> iterators;
while (begin != end) {
iterators.push(begin);
++begin;
}
while (!iterators.empty()) {
Foo::const_iterator i = iterators.top();
// do something with *i
iterators.pop();
}
This code requires at least a ForwardIterator (it will not work with a mere InputIterator).
Yes, you can use std::reverse_iterator. It is also a good idea not
to specify the type of the iterator explicitly. Use a template
argument instead.
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
template <typename BidirectionalIterator>
void x(BidirectionalIterator b, BidirectionalIterator e) {
std::reverse_iterator<BidirectionalIterator> rb(e), re(b);
std::for_each(rb, re,
[](typename BidirectionalIterator::reference x)
{ std::cout << x << std::endl;});
}
int main()
{
std::vector<int> v{1,2,3,4};
x(begin(v), end(v));
return 0;
}