Printing Standard Library Sequence Containers in C++ 1998/2003
In C++ 1998/2003, the Standard Library Sequence Containers (i.e. deque, list, and vector) could be printed using the following template:
template < template < typename, typename > class C, typename T, template < typename > class A > inline std::ostream & operator << ( std::ostream & o, C< T, A< T > > const & c ) { o << '{'; typename C< T, A< T > >::const_iterator i = c.begin(); typename C< T, A< T > >::const_iterator end = c.end(); if ( i != end ) { o << ' ' << *i; for ( ++i; i != end; ++i ) o << ", " << *i; o << ' '; } o << '}'; return o; }
For instance, if we had the following main.cpp:
#include <iostream> #include <string> #include <deque> #include <list> #include <vector> template < template < typename, typename > class C, typename T, template < typename > class A > inline std::ostream & operator << ( std::ostream & o, C< T, A< T > > const & c ) { o << '{'; typename C< T, A< T > >::const_iterator i = c.begin(); typename C< T, A< T > >::const_iterator end = c.end(); if ( i != end ) { o << ' ' << *i; for ( ++i; i != end; ++i ) o << ", " << *i; o << ' '; } o << '}'; return o; } int main () { std::deque< int > d; d.push_back( 1 ); d.push_back( 2 ); d.push_back( 3 ); std::cout << "d: " << d << '\n'; std::list< std::string > l; l.push_back( "a" ); l.push_back( "b" ); l.push_back( "c" ); std::cout << "l: " << l << '\n'; std::vector< double > v; v.push_back( 1.1 ); v.push_back( 2.2 ); v.push_back( 3.3 ); std::cout << "v: " << v << '\n'; return 0; }
And we executed the following command:
g++ -o main.exe main.cpp -march=native -O3 -Wall -Wextra -Werror && ./main.exe
Then we would get the following output:
d: { 1, 2, 3 } l: { a, b, c } v: { 1.1, 2.2, 3.3 }
NOTE: Even though “C” stood for container, “T” stood for type, and “A” stood for allocator in the template, any class or struct that had the form Type1< Type2, Type3< Type2 > > would instantiate this template … and then ::const_iterator, .begin(), and .end() would need to make sense for that class or struct.
Of course, if we provided an allocator that didn’t match the form A<T>, then the template above wouldn’t be instantiated; therefore, the Standard Library Sequence Containers could also be printed using the following simpler template in C++ 1998/2003:
template < template < typename, typename > class C, typename T, typename A > inline std::ostream & operator << ( std::ostream & o, C< T, A > const & c ) { o << '{'; typename C< T, A >::const_iterator i = c.begin(); typename C< T, A >::const_iterator end = c.end(); if ( i != end ) { o << ' ' << *i; for ( ++i; i != end; ++i ) o << ", " << *i; o << ' '; } o << '}'; return o; }
The revised main.cpp would be compiled and the resulting main.exe executed similarly.
Unfortunately, we needed to write another template for Standard Library Associative Containers … unless “typelist” templates were used.
In C++ 2011, variadic templates can be used to print both Standard Library Sequence and Associative Containers using one template … as we will see in the next post.