код infix_iterator
Я ранее размещал это в Stack Overflow и рассматриваю возможность его отправки в Boost для более широкого распространения, но, возможно, было бы лучше сначала поставить его для экспертной оценки, и посмотреть, есть ли четкие улучшения, которые могут сделайте первым.
// infix_iterator.h
//
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>
template <class T,
class charT=char,
class traits=std::char_traits<charT> >
class infix_ostream_iterator :
public std::iterator<std::output_iterator_tag,void,void,void,void>
{
std::basic_ostream<charT,traits> *os;
charT const* delimiter;
bool first_elem;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;
infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{}
infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s),delimiter(d), first_elem(true)
{}
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
{
// Here's the only real change from ostream_iterator:
// We don't print the delimiter the first time. After that,
// each invocation prints the delimiter *before* the item, not
// after. As a result, we only get delimiters *between* items,
// not after every one.
if (!first_elem && delimiter != 0)
*os << delimiter;
*os << item;
first_elem = false;
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator*() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++(int) {
return *this;
}
};
#endif
Это (по крайней мере, предназначено) для значительной замены для std::ostream_iterator
, единственное отличие состоит в том, что (по крайней мере, при обычном использовании) он выводит только разделители между , а не после каждого элемента. Используемый код выглядит примерно так:
#include "infix_iterator.h"
std::vector<int> numbers = {1, 2, 3, 4};
std::copy(begin(numbers), end(numbers),
infix_ostream_iterator<int>(std::cout, ", "));
Мотивация для этого довольно проста - с std::ostream_iterator
, ваш список будет выглядеть как 1, 2, 3, 4,
, но с infix_iterator, он выводится как 1, 2, 3, 4
.
В качестве примечания, хотя я использовал пару функций C ++ 11 в этом демо-коде, я считаю, что итератор должен быть в порядке с C ++ 03 - хотя, если кто-то увидит что-нибудь, что будет проблема для компилятора без поддержки C ++ 11, я тоже хотел бы услышать об этом.
Изменить: В случае, если кто-то заботится, вот новая версия, включающая входные данные от @Konrad и @Loki. Спасибо вам обоим.
// infix_iterator.h
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>
#include <string>
template <class T, class charT=char, class traits=std::char_traits<charT> >
class infix_ostream_iterator :
public std::iterator<std::output_iterator_tag, void, void, void, void>
{
std::basic_ostream<charT,traits> *os;
std::basic_string<charT> delimiter;
std::basic_string<charT> real_delim;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT, traits> ostream_type;
infix_ostream_iterator(ostream_type &s)
: os(&s)
{}
infix_ostream_iterator(ostream_type &s, charT const *d)
: os(&s),
real_delim(d)
{}
infix_ostream_iterator<T, charT, traits> &operator=(T const &item)
{
*os << delimiter << item;
delimiter = real_delim;
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator*() {
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator++() {
return *this;
}
infix_ostream_iterator<T, charT, traits> &operator++(int) {
return *this;
}
};
#endif
6 ответов
Ничего важного (только некоторые личные мнения):
Согласованный интервал
infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{} // ^^^ No Space ^^^Trailing space
Согласование типа Именование
// Here we have & on the left
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
// Here we have it on the right
infix_ostream_iterator<T,charT,traits> &operator*() {
Написание кода delimter
Я очень сильно ошибаюсь при написании этого => delimiter
Легче читать список инициализаций
Так же, как я предпочитаю один оператор в строке, я предпочитаю одну переменную, инициализированную на строку в списке инициализаторов (легче читать).
infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s)
, delimiter(d)
, first_elem(true)
{}
Удалите if из основного корпуса.
Вероятно, это не имеет никакого значения для производительности, но я бы удалил if
, чтобы код выглядел следующим образом:
*os << delimter << item;
delimter = actualDelimter;
return *this;
При построении actualDelimter
точки в строке, предоставленной пользователем (или пустой строкой) и delimter
, указывают на пустую строку.
Единственное, что я могу критиковать в коде, это несогласованное размещение пробелов между операторами-инфиксами и объявлениями указателей /ссылок.
class charT=char,
public std::iterator<std::output_iterator_tag,void,void,void,void>
â € | и т. д., пробелы.
: os(&s),delimiter(0), first_elem(true)
â € | и т. д., непоследовательное использование пробелов.
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
infix_ostream_iterator<T,charT,traits> &operator*() {
â € | и т. д., Несогласованное размещение &
.
Iâ € ™ d также унифицирует использование пустых строк между определениями функций и удаляет пустую строку между блоком template <…>
и заголовком класса, а также конец определения класса.
Ничтожное, конечно, но это буквально единственное, что критиковать.
Я также объявляю первый конструктор как явный:
explicit infix_ostream_iterator(ostream_type &s)
: os(&s)
{}
Хотя я могу себе представить, что использование такого итератора весьма ограничено, таким образом вы не гарантируете, что не будет никакого нежелательного преобразования типов.
Хранить указатели const char
Я согласен с предложенными Lokis изменениями, за исключением того, что я оставил delimiter
и real_delim
как charT const *
и изменил конструктор на:
infix_ostream_iterator(ostream_type &s, charT const *d = 0)
: os(&s)
, delimiter("")
, real_delim(d ? d : "")
{
}
Таким образом мы уменьшаем стоимость копирования строк, вместо этого мы просто копируем указатель.
Выведенные типы
Подражая изменениям, внесенным в std :: less в C + +14, вы можете сделать шаблон operator=
, поэтому нам не нужно указывать этот тип для класса. Это приведет к потере совместимости с интерфейсом std::ostream_iterator
.
template <class T>
infix_ostream_iterator<charT, traits> &operator=(T const &item)
{
*os << delimiter << item;
delimiter = real_delim;
return *this;
}
Прежде всего, хорошая идея с этим классом! Я согласен с изменениями, уже предложенными другими пользователями (Loki, Konrad, user15108, dalle).
Кроме того, я заметил, что infix_ostream_iterator
не является конструктивным по умолчанию. Это подходит для использования и следует за дизайном std::ostream_iterator
. Поэтому вы можете сохранить std::basic_ostream
в качестве переменной-члена ссылки. То есть
class infix_ostream_iterator : ... {
...
std::basic_ostream<charT,traits>& os;
...
}
Теперь вы можете удалить указатель, который упростит оставшийся код. Например, конструктор становится
infix_ostream_iterator(ostream_type &s, charT const *d)
: os(s)
, real_delim(d)
{}
и вы можете напрямую писать в поток
os << delimiter << item;
Это деталь реализации (ваш интерфейс такой же). Я считаю, что компилятор будет генерировать один и тот же набор инструкций.
Ожидаем увидеть этот класс в чем-то вроде Boost! Удачи вам в подаче.
Я знаю, что я много лет опаздываю на это, но я хочу выкинуть идею: почему дела с разделителями и без разделителей должны быть одного типа? Ваша первоначальная версия использовала charT const*
, а ваша новая версия использует std::basic_string<charT>
... но кажется расточительным иметь две строки string
s, которые мы копируем, чтобы обрабатывать не имеющие разделителя.
Я предлагаю иметь перегруженную функцию make_*
, которая либо создает ваш итератор, либо создает стандартный:
// no delimiter
template <class T, class CharT, class Traits>
std::ostream_iterator<T, CharT, Traits>
make_delim_iterator(std::basic_ostream<CharT,Traits>& );
// yes, delimiter
template <class T, class CharT, class Traits>
infix_ostream_iterator<T, CharT, Traits>
make_delim_iterator(std::basic_ostream<CharT,Traits>&, CharT const*);
Это позволит вам иметь два члена CharT const*
вместо двух basic_string<CharT>
, и ваш тип всегда использует разделитель.