Marti Nito
Marti Nito

Reputation: 814

Range based for with pair<Iterator,Iterator>

I have a question with respect to the following answer:

https://stackoverflow.com/a/15828866/2160256

As stated there, we cannot use range based for with BGL like this:

   for(auto e : boost::edges(g))
       // do something with e

However, here it states, that we can overload the begin() and end() functions that are required to use range based for semantics. So I tried:

   template<class I>
   I begin(std::pair<I,I>& p)
   { return p.first;}

   template<class I>
   I end(std::pair<I,I>& p)
   { return p.second;}

However, the compiler still complains:

error: no matching function for call to ‘begin(std::pair<some_really_ugly_type,some_really_ugly_type>&)

What am I doing wrong? Does the name lookup not work? Or is this not possible after all? I also found this answer, which works, but shouldtn't it be possible with the begin/end free function overlods as well? Regards, Marti

BTW: I find it really tiresome to write

   typename Graph::edge_iterator ebegin, eend;
   std::tie(ebegin,eend) = boost::edges(_graph);
   std::for_each(ebegin,eend,[&](const edge_descriptor& e){/*do something with e*/;});

UPDATE: C++17 should now allow the following :-)

auto [ebegin,eend] = boost::edges(_graph);

Upvotes: 9

Views: 10903

Answers (6)

klaus triendl
klaus triendl

Reputation: 1359

C++20 Ranges library

With the advent of C++20 ranges, you can now use a subrange (which @Slava lamented the lack of), which doesn't require custom helpers (like @sehe's answer):

for (auto [it, end] : boost::edges(_graph);
     const edge_descriptor& edge : std::ranges::subrange(it, end)) {
    // ...
}

Upvotes: 2

fwyzard
fwyzard

Reputation: 2687

I would expand on Barry's answer and suggest (until C++17) to add

template <typename I>
iter_pair<I> make_range(std::pair<I, I> p) {
    return iter_pair<I>(p);
}

to be used as

for (auto e : make_range(boost::edges(g)))
    // do something with e

Upvotes: 2

sehe
sehe

Reputation: 392863

Iterator pairs are not ranges by design! The idea was specifically rejected from the language and library specification. See e.g.

If you "find it tiresome" to write the tie() workaround, just use

for (auto& edge : make_iterator_range(boost::edges(_graph)))
    /*do something with edge*/;

You could alias boost::make_iterator_range something shorter, but my editor¹ suggests make_iterator_range as completion when I type mir. This is plenty speedy for me


¹ of course, that editor is Vim

Upvotes: 8

JDługosz
JDługosz

Reputation: 5642

Since the Boost FOREACH macro uses C++03 declarations and explicit template code, it ought to work with conventional lookup rules instead of special for rules.

You might make sure that it expands the old way even though range-based for is available.

Another approach would be to make your own class that is derived from the pair, but contains the begin and end members. Then write

for (e: mypair(p))

Instead of

for (e: p)

Upvotes: 0

Barry
Barry

Reputation: 302718

You cannot have free begin() and end() functions as per T.C.'s answer. However, what you can do, is just make your own class and add member begin and end to it:

template <typename I>
struct iter_pair : std::pair<I, I>
{ 
    using std::pair<I, I>::pair;

    I begin() { return this->first; }
    I end() { return this->second; }
};

And just use that instead of a normal pair:

std::vector<int> v = {1, 2, 3, 4, 5};

iter_pair<decltype(v.begin())> pr{v.begin(), v.end()};

for (int i : pr) {
    std::cout << i << ' ';
}
std::cout << std::endl;

Upvotes: 4

T.C.
T.C.

Reputation: 137310

In a range-based for loop, name lookup for non-member begin() and end() uses ADL only. It doesn't perform ordinary unqualified lookup. §6.5.4 [stmt.ranged]/p1.3:

  • if _RangeT is a class type, the unqualified-ids begin and end are looked up in the scope of class _RangeT as if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, [...]

  • otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up in the associated namespaces (3.4.2). [ Note: Ordinary unqualified lookup (3.4.1) is not performed. —end note ]

Hence, your begin() and end() overloads are not found.

Upvotes: 5

Related Questions