Defining a proxy-based OutputIterator in terms of

2019-06-09 05:57发布

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).

However, Boost complains:

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>.

1条回答
乱世女痞
2楼-- · 2019-06-09 06:38

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 use void* instead of void. (Or perhaps for consistency use proxy& and define it outside the class).

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; }
};
查看更多
登录 后发表回答