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 < typenametypename > class Ctypename Ttemplate < typename > class A >
inline std::ostream & operator << ( std::ostream & oCTAT > > const & c ) {
    o << '{';
    typename CTAT > >::const_iterator i = c.begin();
    typename CTAT > >::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 < typenametypename > class Ctypename Ttemplate < typename > class A >
inline std::ostream & operator << ( std::ostream & oCTAT > > const & c ) {
    o << '{';
    typename CTAT > >::const_iterator i = c.begin();
    typename CTAT > >::const_iterator end = c.end();
    if ( i != end ) {
        o << ' ' << *i;
        for ( ++i; i != end; ++i )
            o << ", " << *i;
        o << ' ';
    }
    o << '}';
    return o;
}
 
int main () {
 
    std::dequeint > 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::vectordouble > 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 < typenametypename > class Ctypename Ttypename A >
inline std::ostream & operator << ( std::ostream & oCTA > const & c ) {
    o << '{';
    typename CTA >::const_iterator i = c.begin();
    typename CTA >::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 *