N3581
2013-03-16
Mike Spertus, Symantec
mike_spertus@symantec.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 delimeter.
We offer two alternate proposals
Add a constructor to ostream_iterator that also takes a boolean
argument saying whether you want a suffix. We can now easily print a vector correctly:
vector<int> v = {1, 4, 6};
cout << "(";
copy(v.begin(), v.end(), ostream_iterator<int>(cout, ", ", false));
cout << ")"; // Prints (1, 4, 6) as desired
In case we would rather not modify ostream_iterator, we could add
a new delimited_iterator 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(), delimited_iterator<int>(cout, ", "));
cout << ")"; // Prints (1, 4, 6) as desired
ostream_iterator is defined as:Modify §24.6.2.1 [ostream.iterator.cons.des] as follows:namespace std { template <class T, class charT = char, class traits = char_traits<charT> > class ostream_iterator: 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_iterator(ostream_type& s); ostream_iterator(ostream_type& s, const charT* delimiter); ostream_iterator(ostream_type& s, const charT* delimiter, bool is_suffix); ostream_iterator(const ostream_iterator<T,charT,traits>& x); ~ostream_iterator(); ostream_iterator<T,charT,traits>& operator=(const T& value); ostream_iterator<T,charT,traits>& operator*(); ostream_iterator<T,charT,traits>& operator++(); ostream_iterator<T,charT,traits>& operator++(int); private: basic_ostream<charT,traits>* out_stream; // exposition only const charT* delim; // exposition only bool suf; // exposition only bool first_element; // exposition only }; }
ostream_iterator(ostream_type& s, const charT* delimiter);Modify §24.6.2.2 [ostream.iterator.ops] as follows:Effects: Initializes out_stream with &s, and delim with delimiter, suf with false, and first_element with true.ostream_iterator(ostream_type& s, const charT* delimiter, bool is_suffix);Effects: Initializes out_stream with &s, delim with delimiter, suf with is_suffix, and first_element with true.ostream_iterator(const ostream_iterator& x);Effects: Constructs a copy of x.
ostream_iterator& operator=(const T& value);Effects:if(delim != 0 && !suf) { if(!first_element) out_stream << delim; first_element = false; } *out_stream << value; if(delim != 0 && suf) *out_stream << delim; return (*this);
template <class T, class charT = char, class traits = char_traits<charT> >
class ostream_iterator;
template <class T, class charT = char, class traits = char_traits<charT> >
class delimited_iterator;
template<class charT, class traits = char_traits<charT> >
class istreambuf_iterator;
Add a section §24.6.x named “Class template delimited_iterator” [delimited.iterator] 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 delimited_iterator
mutatis mutandis
except as follows:
Make §24.6.x [delimited.iterator] as follows:
delimited_iterator is defined as:The corresponding portion of §24.6.x.1 [delimited.iterator.cons.des] should look as follows:namespace std { template <class T, class charT = char, class traits = char_traits<charT> > class delimited_iterator: 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_iterator(ostream_type& s); delimited_iterator(ostream_type& s, const charT* delimiter); delimited_iterator(const ostream_iterator<T,charT,traits>& x); ~delimited_iterator(); delimited_iterator<T,charT,traits>& operator=(const T& value); delimited_iterator<T,charT,traits>& operator*(); delimited_iterator<T,charT,traits>& operator++(); delimited_iterator<T,charT,traits>& operator++(int); private: basic_ostream<charT,traits>* out_stream; // exposition only const charT* delim; // exposition only bool first_element; // exposition only }; }
delimited_iterator(ostream_type& s, const charT* delimiter);The corresponding portion §24.6.x.2 [delimited.iterator.ops] should look as follows:Effects: Initializes out_stream with &s, delim with delimiter, and first_element with true.delimited_iterator(ostream_type& s, const charT* delimiter, bool is_suffix);Effects: Initializes out_stream with &s, delim with delimiter and first_element with true.delimited_iterator(const delimited_iterator& x);Effects: Constructs a copy of x.
delimited_iterator& operator=(const T& value);Effects:if(delim != 0) { if(!first_element) out_stream << delim; first_element = false; } *out_stream << value; return (*this);