简体   繁体   中英

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

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

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

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-id s 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.

You cannot have free begin() and end() functions as per TC'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;

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

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM