N4066
Revision of N4007
2014-06-18
Mike Spertus, Symantec
mike_spertus@symantec.com
Nathan Wilson
nwilson20@gmail.com
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.
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
In the LEWG discussion of this proposal, a delimiter style such as prefix, infix, and suffix was presented.
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.
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.copy(v.begin(), v.end(), std::ostream_joiner(cout, ", "));
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://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_joinermake_ostream_joiner(basic_ostream<charT, traits>& os, basic_string<charT, traits> delimiter); template<class charT, class traits = char_traits<charT> > class istreambuf_iterator;
ostream_joiner is defined as:The corresponding portion of §24.6.x.1 [ostream.joiner.cons.des] should look as follows: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 }; }
ostream_joiner(ostream_type& s, basic_string<charT, traits> delimiter);The corresponding portion §24.6.x.2 [ostream.joiner.ops] should look as follows: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 of §24.6.x.3 [ostream.joiner.creation] 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);
template <class charT, class traits> ostream_joinerNote: 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>make_ostream_joiner(basic_ostream<charT, traits>& os, basic_string<charT, traits> delimiter); Returns: ostream_joiner<charT, traits>(os, delimiter);