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.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="">