Определение основанного на прокси OutputIterator с точки зрения boost::iterator_facade

Я написал этот код на C++17 и ожидал, что он будет работать "из коробки".

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; }
};

Я пытаюсь соответствовать поведению всех стандартных OutputIterators, устанавливая typedefs члена моего итератора value_type а также reference в void (так как эти типы не имеют смысла для итератора, чей operator* не возвращает ссылку).

Однако Буст жалуется:

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

Похоже, Boost пытается жестко кодировать сгенерированный operator*подпись как reference operator*() const, То есть, boost::iterator_facade может вывести правильный тип возвращаемого значения operator*() просто передавая то, что было возвращено dereference(); но по какой-то причине это просто не подыгрывает.

Какое решение? Я не могу пройти proxy в качестве параметра шаблона базового класса, так как proxy еще не определено. Я мог вытащить proxyв пространство имен деталей:

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; }
};

но это кажется неловким и определенно то, что "не должно быть необходимым".

Это ошибка в iterator_facade? Это особенность, а не ошибка? Если последнее, то как я должен использовать его для создания OutputIterators?

Кроме того, незначительный придирки: даже мой обход с пространством имен детализации "неправильный" в том смысле, что он делает std::is_same_v<putc_iterator::reference, detail::proxy> когда то, что я хочу (для паритета со стандартными итераторами) std::is_same_v<putc_iterator::reference, void>,

1 ответ

Boost Iterator Facade был хорош в то время, но сейчас он устарел, так как не очень гибок (он плохо сочетается с auto и с ссылками на r-значение, которые в принципе могут быть созданы путем разыменования итератора r-значения). Я не повторяю концепцию фасада, но она может быть обновлена ​​до C++11.

Кроме того, теперь с C++ 11 проще написать итератор с нуля.

Во всяком случае, если вам нужно определить reference просто чтобы соответствовать аргументам, которые будут переданы, (и если вы обещаете не использовать его), вы можете использовать void* вместо void, (Или возможно для использования последовательности proxy& и определить его вне класса).

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; }
};
Другие вопросы по тегам