Reputation: 15905
I have a class that applies some boost transform adaptors to a range (for sake of example, in reality it's a lot more complex than this):
struct Foo {
auto range() const {
return boost::irange(0, 10)
| boost::adaptors::transformed([] (auto x) { return x * 2; });
}
auto begin() const { return range().begin(); }
auto end() const { return range().end(); }
};
This alone allows us to iterate over a Foo
using a range for:
for (auto x : Foo()) {
std::cout << num << std::endl;
}
However, this doesn't compose well with other boost adaptors or range operations (like boost::join
):
auto bad = boost::join(boost::irange(0, 10), Foo());
auto also_bad = Foo() | boost::adaptors::transformed([] (auto x) { return x + 1; });
Both of the above provoke some nasty template errors. The former (bad
):
In file included from test.cpp:4:
/usr/local/include/boost/range/join.hpp:30:70: error: no type named 'type' in
'boost::range_iterator<const Foo, void>'
BOOST_DEDUCED_TYPENAME range_iterator<SinglePassRange1>::type,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/join.hpp:44:28: note: in instantiation of template class
'boost::range_detail::joined_type<const Foo, const boost::integer_range<int> >' requested
here
: public range_detail::joined_type<SinglePassRange1, SinglePassRange2>::type
^
test.cpp:34:16: note: in instantiation of template class 'boost::range::joined_range<const Foo,
const boost::integer_range<int> >' requested here
auto bad = boost::join(Foo(), range);
^
...
And the latter (also_bad
):
In file included from test.cpp:1:
In file included from /usr/local/include/boost/range/any_range.hpp:17:
In file included from /usr/local/include/boost/range/detail/any_iterator.hpp:22:
In file included from /usr/local/include/boost/range/detail/any_iterator_wrapper.hpp:16:
In file included from /usr/local/include/boost/range/concepts.hpp:24:
/usr/local/include/boost/range/value_type.hpp:26:70: error: no type named 'type' in
'boost::range_iterator<Foo, void>'
struct range_value : iterator_value< typename range_iterator<T>::type >
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/adaptor/replaced.hpp:109:40: note: in instantiation of template
class 'boost::range_value<Foo>' requested here
BOOST_DEDUCED_TYPENAME range_value<SinglePassRange>::type>& f )
^
test.cpp:35:27: note: while substituting deduced template arguments into function template
'operator|' [with SinglePassRange = Foo]
auto also_bad = Foo() | boost::adaptors::transformed([] (auto x) { return x * 2; });
^
...
Both of the errors seem to be complaining that Foo
isn't a range. I've tried adding an operator OutContainer()
and typedefs for iterator
/const_iterator
as suggested here to no avail. What must I do to Foo
to allow it to play nicely with these range operations?
Upvotes: 0
Views: 648
Reputation: 303067
The error is super helpful in this case. Your type has to model SinglePassRange
and it does not. There's a page about how to do this, and while you provided begin()
and end()
, you did not provide type aliases for iterator
and const_iterator
. Hence, you don't model SinglePassRange
.
But it's actually quite good that your code failed, because it is also bad. You have begin()
call range().begin()
and end()
call range().end()
. Those are iterators into different ranges. So this is undefined behavior anyway - you're failing to meet the semantic concepts of a SinglePassRange
.
The easier solution is to just use Foo()
directly. This already works:
auto good = boost::join(boost::irange(0, 10), Foo().range());
auto also_good = Foo().range() | boost::adaptors::transformed([] (auto x) { return x + 1; });
And means you just have to write one function: range()
Upvotes: 3