I want to create a range from two iterators returned by a function.
I used the answer to a related question to create a new subrange using range-v3:
auto [it1, it2] = out_edges(u, _graph);
return ranges::subrange(it1, it2) | ranges::views::transform([](auto it){return it->target();});
But my compiler tells me: error: no viable constructor or deduction guide for deduction of template arguments of 'subrange'
I don't understand the error and do not know how I am supposed to define the required deduction guide.
The iterator type is the following:
class out_edge_iterator
: public boost::iterator_adaptor<out_edge_iterator,
vertex_descriptor const *,
edge_descriptor,
forward_traversal_tag,
edge_descriptor>
{
vertex_descriptor const *last;
vertex_descriptor source;
public:
out_edge_iterator(Vertex const *first, Vertex const *last, Vertex source)
: out_edge_iterator::iterator_adaptor_(first), last(last),
source(source)
{
BOOST_ASSERT(source != null_vertex());
post_increment();
}
private:
edge_descriptor dereference() const
{
return edge_descriptor(source, *this->base_reference());
}
void post_increment()
{
while (this->base_reference() != last
&& *this->base_reference() == null_vertex())
{
this->base_reference()++;
}
}
void increment()
{
this->base_reference()++;
post_increment();
}
friend class boost::iterator_core_access;
};
I have three version, just using Boost, using std::ranges
and using Range-v3.
To cut the corners, I always write boost::make_iterator_range(out_edges(e, g))
which works fine and can treat the pair as a range:
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;
int main() {
Graph g;
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(1, 3, g);
add_edge(1, 4, g);
add_edge(2, 5, g);
add_edge(3, 6, g);
for (auto v : boost::make_iterator_range(vertices(g))) {
std::cout << v << " ->";
for (auto e : make_iterator_range(out_edges(v, g))) {
std::cout << " " << e;
}
std::cout << "\n";
}
}
Sometimes you might need to
#include <boost/range/iterator_range.hpp>`
std::ranges
In C++20, you can use std::ranges:
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
#include <ranges>
using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;
int main() {
Graph g;
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(1, 3, g);
add_edge(1, 4, g);
add_edge(2, 5, g);
add_edge(3, 6, g);
auto [vb,ve] = vertices(g);
for (auto v : std::ranges::subrange(vb, ve)) {
std::cout << v << " ->";
auto [eb, ee] = out_edges(v, g);
for (auto e : std::ranges::subrange(eb, ee)) {
std::cout << " " << e;
}
std::cout << "\n";
}
}
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
#include <range/v3/all.hpp>
namespace r = ::ranges::v3;
using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;
int main() {
Graph g;
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(1, 3, g);
add_edge(1, 4, g);
add_edge(2, 5, g);
add_edge(3, 6, g);
auto [vb,ve] = vertices(g);
for (auto v : r::subrange(vb, ve)) {
std::cout << v << " ->";
auto [eb, ee] = out_edges(v, g);
for (auto e : r::subrange(eb, ee)) {
std::cout << " " << e;
}
std::cout << "\n";
}
}
All three code samples have identical output:
0 -> (0,1)
1 -> (1,2) (1,3) (1,4)
2 -> (2,5)
3 -> (3,6)
4 ->
5 ->
6 ->