6

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);
Marti Nito
  • 697
  • 5
  • 17
  • Think twice before doing this. There are good reasons not to make the assumption that any iterator pair is-a range. See my answer for my favourite idiom. – sehe Nov 26 '14 at 08:45
  • @sehe good reasons suggested adding type `range`, but standard did neither, they did not add range and they did not allow par of iterators to be used in for range loop. Good job! – Slava Apr 06 '17 at 13:24

5 Answers5

7

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

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Thanks, i do not have problems with verbose oneliners, but those three liners do hurt :-) I replaced my own make_range with calls to boost :) – Marti Nito Nov 29 '14 at 15:44
5

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
3

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;
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
2

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
fwyzard
  • 2,364
  • 1
  • 21
  • 19
  • The reason I suggest a helper function is to allow it to deduce the template type instead of declaring it explicitly: `for (auto e : make_range(boost::edges(g)))` instead of `for (auto e : iter_pair>(boost::edges(g)))` where you have to guess the correct type for `>` . C++17 should introduce [class_template_deduction](http://en.cppreference.com/w/cpp/language/class_template_deduction) in constructor calls, alleviating the need for the helper function. – fwyzard Jan 30 '17 at 10:24
0

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)
JDługosz
  • 5,592
  • 3
  • 24
  • 45