код 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 
57 голосов | спросил Jerry Coffin 29 J0000006Europe/Moscow 2012, 09:32:19

6 ответов


23

Ничего важного (только некоторые личные мнения):

Согласованный интервал

    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, указывают на пустую строку.

ответил Martin York 30 J0000006Europe/Moscow 2012, 03:41:36
13

Единственное, что я могу критиковать в коде, это несогласованное размещение пробелов между операторами-инфиксами и объявлениями указателей /ссылок.

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 <…> и заголовком класса, а также конец определения класса.

Ничтожное, конечно, но это буквально единственное, что критиковать.

ответил Konrad Rudolph 29 J0000006Europe/Moscow 2012, 16:07:53
7

Я также объявляю первый конструктор как явный:

explicit infix_ostream_iterator(ostream_type &s)
        : os(&s)
    {}

Хотя я могу себе представить, что использование такого итератора весьма ограничено, таким образом вы не гарантируете, что не будет никакого нежелательного преобразования типов.

ответил user15108 21 J000000Saturday12 2012, 22:23:03
4

Хранить указатели 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;
}
ответил dalle 22 Jpm1000000pmWed, 22 Jan 2014 12:57:04 +040014 2014, 12:57:04
3

Прежде всего, хорошая идея с этим классом! Я согласен с изменениями, уже предложенными другими пользователями (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! Удачи вам в подаче.

ответил Frederik Aalund 1 62014vEurope/Moscow11bEurope/MoscowSat, 01 Nov 2014 22:06:44 +0300 2014, 22:06:44
2

Я знаю, что я много лет опаздываю на это, но я хочу выкинуть идею: почему дела с разделителями и без разделителей должны быть одного типа? Ваша первоначальная версия использовала 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>, и ваш тип всегда использует разделитель.

ответил Barry 11 FriEurope/Moscow2015-12-11T19:21:12+03:00Europe/Moscow12bEurope/MoscowFri, 11 Dec 2015 19:21:12 +0300 2015, 19:21:12

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132