N4066
Revision of N4007
2014-06-18
Mike Spertus, Symantec
mike_spertus@symantec.com
Nathan Wilson
nwilson20@gmail.com

# Delimited iterators (Rev. 3)

It is extremely tempting to use `ostream_iterator` to, say, print a vector like: `vector<int> v = {1, 4, 6}; cout << "("; copy(v.begin(), v.end(), ostream_iterator<int>(cout, ", ")); cout << ")"; // Oops! Prints (1, 4, 6, )`

The problem is that the “delimiter” in the `ostream_iterator` constructor call is better described as a suffix than a delimiter.

## Proposal

We propose a new `ostream_joiner` class that acts like `ostream_iterator` except that the delimiter is only placed between output elements: `vector<int> v = {1, 4, 6}; cout << "("; copy(v.begin(), v.end(), std::make_ostream_joiner(cout, ", ")); cout << ")"; // Prints (1, 4, 6) as desired`

## Discussion

In the LEWG discussion of this proposal, a delimiter style such as prefix, infix, and suffix was presented.

- Since `ostream_iterator` already achieves a suffix delimiter style, the authors have removed this support in favour of the infix style defined in the class. We have also changed the use of const char* as the delimiter for basic_string<charT, traits>.

In the LEWG discussion of N3581 in Issaquah, it was pointed out that the interactions of this proposal were unexpectedly subtle, so the authors wish to discuss the design decisions in more detail.

- As pointed out in LEWG, output iterators can be defined as having `void` value type (§24.4.1p1), so the type being iterated need not be specified. It is apparently included in `ostream_iterator` for stylistic reasons only. Since brevity is of the essence in most uses of convenience classes such as this, we choose to omit the value type from `ostream_joiner`.
- Since we are changing template parameters, we reject Option 1 from N3581(add functionality to `ostream_iterator` instead of creating a new class) and create a new class instead.
- Since `ostream_joiner` is necessarily stateful and iterators are CopyConstructible and CopyAssignable, (§24.2.2p2), we need to consider what happens when `ostream_joiner`s are copied. According to §24.2.4p2, “Assignment through the same value of the iterator happens only once.” (Emphasis in the original). At Issaquah, this was interpreted as meaning that writing to one copy of an output iterator invalidates writing through the original output iterator. This conveniently means that the `ostream_joiner` can store whether we are outputting the first element as a member, without the need for undesirable shared state (although it is not prohibited).
- Another possible variant would be to have a separate prefix, infix, and suffix to the list. `copy(v.begin(), v.end(), std::ostream_joiner(cout, {"(", ", ", ")"}));` On the one hand, this may be gilding the lily. On the other hand, most of the uses we have run into would leverage this and those that don't could use the simpler notation, and it obviates the need for the annoying `delimiter_style` enum.
- We provide a `make_ostream_joiner` function to simplify the deduction of template parameters as suggested by LEWG.
- If Template parameter deduction for constructors is eventually adopted, we will be able to dispense with the `make_` prefix for notation that will be more concise without sacrificing clarity:`copy(v.begin(), v.end(), std::ostream_joiner(cout, ", "));`

## Implementation

Straightforward. I give my students a homework assignment to implement `ostream_joiner` every year.

## Wording

Modify §24.3 [iterator.synopsis] as follows:

>     //24.6, stream iterators:
>     template <class T, class charT = char, class traits = char_traits<charT>,
>         class Distance = ptrdiff_t>
>     class istream_iterator;
>     template <class T, class charT, class traits, class Distance>
>       bool operator==(const istream_iterator&;t'T,charT,traits,Distance>& x,
>               const istream_iterator<T,charT,traits,Distance>& y);
>     template <class T, class charT, class traits, class Distance>
>       bool operator!=(const istream_iterator<T,charT,traits,Distance>& x,
>               const istream_iterator<T,charT,traits,Distance>& y);

>     template <class charT = char, class traits = char_traits<charT> >
>         class ostream_iterator;
>     template <class charT = char, class traits = char_traits<charT> >
>         class ostream_joiner;
>     template <class charT, class traits>
>       ostream_joiner make_ostream_joiner(basic_ostream<charT, traits>& os, basic_string<charT, traits> delimiter);
>     template<class charT, class traits = char_traits<charT> >
>     class istreambuf_iterator;

Add a section §24.6.x named “Class template ostream_joiner” [ostream.joiner] between §24.6.2 [ostream.iterator] and §24.6.3 [istreambuf.iterator]. This section should be the same as §24.6.2 with all occurrences of `ostream_iterator` replaced with `ostream_joiner` mutatis mutandis except as follows: Make §24.6.x [ostream.joiner] as follows:

> `ostream_joiner` is defined as:

> >     namespace std {
> >       template <class charT = char, class traits = char_traits<charT> >
> >       class ostream_joiner:
> >         public iterator<output_iterator_tag, void, void, void, void> {
> >       public:
> >         typedef charT char_type;
> >         typedef traits traits_type;
> >         typedef basic_ostream<charT,traits> ostream_type;
> >         ostream_joiner(ostream_type& s, basic_string<charT, traits> delimiter);
> >         ostream_joiner(const ostream_iterator<T,charT,traits>& x);
> >        ~ostream_joiner();
> >         template<typename T>
> >         ostream_joiner<charT,traits>& operator=(const T& value);
> >         ostream_joiner<charT,traits>& operator*();
> >         ostream_joiner<charT,traits>& operator++();
> >         ostream_joiner<charT,traits>& operator++(int);
> >       private:
> >         basic_ostream<charT,traits>* out_stream; // exposition only
> >         basic_string<charT, traits> delim; // exposition only
> >         bool first_element; // exposition only
> >       };
> >     }

The corresponding portion of §24.6.x.1 [ostream.joiner.cons.des] should look as follows:

> `ostream_joiner(ostream_type& s, basic_string<charT, traits> delimiter);`

> > Effects: Initializes out_stream with `&s`, delim with `delimiter`, and first_element with `true`.

> `ostream_joiner(const ostream_joiner& x);`

> > Effects: Constructs a copy of `x`.

The corresponding portion §24.6.x.2 [ostream.joiner.ops] should look as follows:

>     template<typename T>
>     ostream_joiner& operator=(const T& value);
>     Effects:
>     if(!first_element)
>        *out_stream << delim;
>     first_element = false;
>     *out_stream << value;
>     return (*this);

The corresponding portion of §24.6.x.3 [ostream.joiner.creation] should look as follows:

>     template <class charT, class traits>
>       ostream_joiner make_ostream_joiner(basic_ostream<charT, traits>& os, basic_string<charT, traits> delimiter);
>     Returns: ostream_joiner<charT, traits>(os, delimiter);

> Note: the implementation of `make_ostream_joiner` should have sufficient overloads that it can be called with any `delimiter` type that is implicitly convertible to `basic_string<charT, traits>`