Я написал этот код на 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>
,
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; }
};
Других решений пока нет …