I wrote this C++17 code and expected it to work out of the box.
class putc_iterator : public boost::iterator_facade<
putc_iterator,
void,
std::output_iterator_tag
>
{
friend class boost::iterator_core_access;
struct proxy {
void operator= (char ch) { putc(ch, stdout); }
};
auto dereference() const { return proxy{}; }
void increment() {}
bool equal(const putc_iterator&) const { return false; }
};
I'm trying to match the behavior of all the standard OutputIterators by setting my iterator's member typedefs value_type
and reference
to void
(since those types are meaningless for an iterator whose operator*
doesn't return a reference).
In file included from prog.cc:2:
/opt/wandbox/boost-1.63.0/clang-head/include/boost/iterator/iterator_facade.hpp:333:50: error: cannot form a reference to 'void'
static result_type apply(Reference const & x)
^
It looks like Boost is trying to hard-code the generated operator*
's signature as reference operator*() const
. That is, boost::iterator_facade
could deduce the proper return type of operator*()
by simply passing along whatever was returned by dereference()
; but for some reason it's just not playing along.
What's the solution? I can't pass proxy
as a template parameter of the base class since proxy
hasn't been defined yet. I could pull proxy
out into a detail namespace:
namespace detail {
struct proxy {
void operator= (char ch) { putc(ch, stdout); }
};
}
class putc_iterator : public boost::iterator_facade<
putc_iterator,
void,
std::output_iterator_tag,
detail::proxy
>
{
friend class boost::iterator_core_access;
auto dereference() const { return detail::proxy{}; }
void increment() {}
bool equal(const putc_iterator&) const { return false; }
};
but that seems awkward and is definitely something that "shouldn't be necessary."
Is this a bug in iterator_facade
? Is it a feature-not-a-bug? If the latter, then how am I supposed to use it to create OutputIterators?
Also, a minor nitpick: even my workaround with the detail namespace is "wrong" in the sense that it makes std::is_same_v<putc_iterator::reference, detail::proxy>
when what I want (for parity with the standard iterators) is std::is_same_v<putc_iterator::reference, void>
.
Boost Iterator Facade was good at the time, but now it is outdated as it is not very flexible (it doesn't play well with
auto
and with r-value references that in principle can be creating by dereferencing a r-value iterator). I am not againts the facade concept, but it could be upgraded to C++11.In addition now with C++11 is easier to write iterator from scratch.
Anyway, if you need to define a
reference
just to comply with the arguments to be passed, (and if you promise not use it) you can usevoid*
instead ofvoid
. (Or perhaps for consistency useproxy&
and define it outside the class).