Reputation: 814
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
Reputation: 1359
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
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
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
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
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
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-idsbegin
andend
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)
andend(__range)
, respectively, wherebegin
andend
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