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.
Related
While discussing multimap with my students, I noticed a small change that could cut out a bit of boilerplate, and was wondering if anyone had suggested it to the standard committee, and if so what the response was.
The canonical method of iterating over an equal range is (taken from cplusplus.com):
// multimap::equal_range
#include <iostream>
#include <map>
int main ()
{
std::multimap<char,int> mymm;
mymm.insert(std::pair<char,int>('a',10));
mymm.insert(std::pair<char,int>('b',20));
mymm.insert(std::pair<char,int>('b',30));
mymm.insert(std::pair<char,int>('b',40));
mymm.insert(std::pair<char,int>('c',50));
mymm.insert(std::pair<char,int>('c',60));
mymm.insert(std::pair<char,int>('d',60));
std::cout << "mymm contains:\n";
for (char ch='a'; ch<='d'; ch++)
{
std::pair <std::multimap<char,int>::iterator,std::multimap<char,int>::iterator> ret;
ret = mymm.equal_range(ch);
std::cout << ch << " =>";
for (std::multimap<char,int>::iterator it=ret.first; it!=ret.second; ++it)
std::cout << ' ' << it->second;
std::cout << '\n';
}
return 0;
}
You cannot use a range based for loop directly in this case because the return type of equal_range is a pair<multimap<K,V>::iterator, multimap<K,V>::iterator>. However, a simple wrapping struct should allow this:
template <typename T>
struct abstract_collection {
abstract_collection(pair<T, T> its)
: m_begin(its.first),
m_end(its.second) {}
abstract_collection(T begin, T end)
: m_begin(begin),
m_end(end) {}
T begin() const { return m_begin; }
T end() const { return m_end; }
T m_begin;
T m_end;
};
Combined with adding a function to the multimap (and others) API to return iterators in this structure, rather than in a pair.
template<typename K, typename V, typename C, typename A>
auto multimap<K, V, C, A>::equal_range_c(K const& k) -> abstract_collection<iterator> {
return equal_range(k);
}
Or alternatively overloading a version of std::begin and std::end that takes a pair of iterators should work as well:
template <typename T>
T begin(pair<T, T> p) { return p.first; }
template <typename T>
T end(pair<T, T> p) { return p.second; }
Has these ideas surfaced before, and if so, what was the committee response? Are they just unworkable or undesirable for some reason I'm not seeing?
(Note, the code was written without attempting to compile or check for expositional purposes only. It's probably wrong. And it doesn't contain typechecking to constrain to iterators only as it ought to, as that was added complexity that didn't serve to explain the idea.)
This is what boost::iterator_range accomplishes, which was adopted into the range library TS as ranges::iterator_range. That TS is to be incorporated sometime after C++17.
In my C++03 code, I have a lot of functions that look like this:
class A {
public:
template <Iterator>
void doSomethingWithObjects(Iterator begin, Iterator end) {
for (Iterator point = begin; point != end; point++) {
mInternal.doSomething(*point);
}
}
private:
DataStructure mInternal;
};
I'm trying to use C++11's features as much as possible in new code, in particular the range-based for loop. My question is, how would I do this with templated iterators? Is there a magic C++ structure that takes two templated iterator types, and turns them into a range expression? In other words, I'm looking for something like this:
class A {
public:
template <Iterator>
void doSomethingWithObjects(Iterator begin, Iterator end) {
static_assert(std::is_same<Point, typename std::decay<Iterator>::type>::value, "wrong type mate!"); // extra credit
for (auto&& point : std::magic(begin, end)) {
mInternal.doSomething(point);
}
}
private:
DataStructure mInternal;
};
If there is a new, preferred ways to do this kind of "add a number of objects to this structure" in C++11, I'm all ears, too.
There's nothing in the standard library. Boost has make_iterator_range, a simplified version of which is trivial to write:
template<class Iterator>
struct iter_range {
Iterator begin_, end_;
Iterator begin() const { return begin_; }
Iterator end() const { return end_; }
};
template<class Iterator>
iter_range<Iterator> make_range(Iterator b, Iterator e) { return {b, e}; }
The original question just called push_back. For that, it doesn't need a loop. Just use the C++03 range overload of insert:
mInternal.insert(mInternal.end(), begin, end);
template<class It>
struct range_t {
It b; It e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::forward<It>(b), std::forward<It>(e)};
}
then:
template <Iterator>
void doSomethingWithObjects(Iterator begin, Iterator end) {
for (auto&& point : range(begin, end)) {
mInternal.doSomething(point);
}
}
and bob is your uncle.
"Range-v3" is a library undergoing the standardization process that contains stuff like this already. Boost also has similar mechanisms.
But this kind of thing is simple enough to roll your own and forget about it. (Better versions include empty, conditionally support size and [], can be constructed from containers and C arrays and anything iterable, know if they are condiguous, conditionally store a copy of the incoming container for reference lifetime extension, etc: but you don't really need any of that).
I would add a function overload and keep the existing function around to make the transition gradual and less disruptive.
template <typename Container>
void doSomethingWithObjects(Container&& c) {
for (auto&& item: c) {
mInternal.doSomething(item);
}
}
You don't actually need to turn them into anything. Just use the iterators with std::for_each:
template <Iterator>
void doSomethingWithObjects(Iterator begin, Iterator end) {
std::for_each(begin, end, [this](auto&& point){
mInternal.doSomething(point);
}
// C++11 version
std::for_each(begin, end, [this](decltype(*begin)& point) {
mInternal.doSomething(point);
}
}
or write the simple loop:
template <Iterator>
void doSomethingWithObjects(Iterator begin, Iterator end) {
for (; begin != end; ++begin) {
mInternal.doSomething(*begin);
}
}
I'm writing an algorithm function that uses iterators. This function should work with both normal and constant iterators, and importantly the class that these iterators come from is NOT a template, I know it in advance.
Is there any way to enforce in the following definition that the iterators come from a specific class?
// This is an example, A could be any other class with exposed iterators.
using A = std::vector<int>;
// How to enforce that Iterator is an iterator from A?
template <typename Iterator>
Iterator foo(Iterator begin, Iterator end);
...
A a;
auto it = foo(a.begin(), a.end());
*it = 4; // Must compile
// --------
const A a;
auto it = foo(a.begin(), a.end());
*it = 4; // Must not compile
// --------
B b;
auto it = foo(b.begin(), b.end()); // Should not compile.
In this case, foo does not modify directly the supplied range, but allows for modification of the result iterator if the supplied range was modifiable in the first place. It would be nice if this could be done without replicating code.
Simply don't use template:
A::iterator foo(A::iterator begin, A::iterator end);
You might use std::enable_if:
#include <type_traits>
#include <vector>
class X : public std::vector<int> {};
class Y : public std::vector<double> {};
template <typename Iterator>
typename std::enable_if<std::is_same<Iterator, X::iterator>()
|| std::is_same<Iterator, X::const_iterator>(),
Iterator>::type
foo(Iterator begin, Iterator end) {
return begin;
}
int main() {
X x0;
auto i0 = foo(x0.begin(), x0.end());
*i0 = 4; // Must compile
const X x1;
auto i1 = foo(x1.begin(), x1.end());
// error: assignment of read-only location
//*i1 = 4; // Must not compile
Y y;
// error: no type named ‘type’ in ‘struct std::enable_if ...
//auto i2 = foo(y.begin(), y.end()); // Should not compile
}
Or static_assert as a nicer alternative:
template <typename Iterator>
Iterator foo(Iterator begin, Iterator end) {
static_assert(std::is_same<Iterator, X::iterator>()
|| std::is_same<Iterator, X::const_iterator>(),
"No X::iteator or X::const_iterator");
return begin;
}
You could use a function overload check:
inline void check_must_be_iterator_from_A(A::iterator) {}
inline void check_must_be_iterator_from_A(A::const_iterator) {}
template <typename I>
I foo(I a, I b) {
typedef void (*must_be_iterator_from_A)(I);
must_be_iterator_from_A c = &check_must_be_iterator_from_A;
//...
}
The other option is to use template specialization to create a constraint, which makes the code within the function terser and definitely without runtime penalty regardless of compiler:
template <typename I> struct is_iterator_from_A;
template <> struct is_iterator_from_A<A::iterator>{ enum {ok}; };
template <> struct is_iterator_from_A<A::const_iterator>{ enum {ok}; };
template <typename I>
I bar(I a, I b) {
is_iterator_from_A<I>::ok;
return a;
}
I would first write your function for const iterators and then write a wrapper around it for the non_const case:
A::const_iterator foo(A::const_iterator start, A::const_iterator end) {
std::cout << " Called foo with const iterators " << std::endl;
return start;
}
A::iterator foo(A::iterator start, A::iterator end) {
std::cout << " Called foo with non_const iterators " << std::endl;
auto it = foo(static_cast<A::const_iterator>(start), static_cast<A::const_iterator>(end));
return start + std::distance(static_cast<A::const_iterator>(start), it);
}
If you inline the wrapper function you should get zero (or nearly zero) overhead.
EDIT:
In case your container doesn't provide random access iterators distance has linear complexity and you will have to use std::advance instead of the "+"-operator, so depending on your performance requirements this might not be a viable solution for you.
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;
}
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;
}