bombax
bombax

Reputation: 1219

C++11: Range-based for loop over range described by templated iterators

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.

Upvotes: 1

Views: 478

Answers (4)

Barry
Barry

Reputation: 303870

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);
    }
}

Upvotes: 0

R Sahu
R Sahu

Reputation: 206717

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);
    }
}

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275896

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).

Upvotes: 2

T.C.
T.C.

Reputation: 137394

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);

Upvotes: 5

Related Questions