Draft 2002-08-28

Chapter 9

Standard Library

The previous eight chapters describe the C++ language. This chapter and the next two describe the library. The library consists of a number of headers, where each header declares types, macros, and functions for use in C++ programs. The standard library typically has external pieces that must be linked into a C++ program, the details of which are dictated by the implementation.

This chapter presents an overview of the standard library and some of its more important components. Chapter 10 introduces the input and output classes, and Chapter 11 describes the containers, iterators, and algorithms that make up what is most often known as the "standard template library" or STL.

Overview of the standard library

The standard library has 50 headers, each containing a set of macro, function, type, and object declarations. A header is sometimes called a header file, but that phrase is misleading. An implementation does not need to implement headers as external files, although it is often simplest to think of a header as a file that contains macro, type, and function declarations.

Almost all the name declared in the standard library are in the std namespace. Macros, of course, are not in any namespace, so it is important that you know which names are macros and which are not. The detailed descriptions in Chapter 13 tell you this information. The only other names that are outside the std namespace are the global new and delete operators, declared in the <new> header.

To use the standard library, you must #include the desired header or headers. Some implementations #include headers within other headers, e.g., <set> might #include <iterator>. The headers that a vendor includes within a header varies from one implementation to another, so code that compiles successfully with one vendor's environment might not compile in a different environment. The solution is to get in the habit of including all the headers you need.

Warning

If you see headers of the form <iostream.h>, that is a warning that the program is very old and was written in the days before the C++ standard. Some compiler vendors try to support old code, but you must realize that such code is not portable. One vendor's backward compatibility library might be based on a 1997 draft of the C++ standard, and another vendor might support a 1996 draft. The only way to have any portability between systems or compilers is to stick with the one official standard.

You must not add any declarations to the std namespace, although you can specialize templates that are declared in std. When you add your own specialization, you must be sure to obey all the constraints specified for the template, and the specialization must depend on at least one user-defined name that has external linkage. Otherwise, the behavior is undefined.

Standard library functions all have external linkage. The C++ functions have extern "C++" linkage. The functions inherited from the C standard can have extern "C" or extern "C++" linkage.

Following are brief descriptions of the contents of each header. The headers inherited from the C standard are marked as "C header." Some of these headers have improved C++ equivalents; the equivalents are also shown. For complete descriptions of these headers, see Chapter 13.

<algorithm>
Standard algorithms for copying, search, sorting, and otherwise operating on iterators and containers. See Chapter 11 for more information about the standard algorithms.
<bitset>
Arbitrarily-sized sequence of bits.
<cassert>
Runtime assertion-checking. C header.
<cctype>
Character classification and case conversion. C header; see <locale>.
<cerrno>
Error codes. C header.
<cfloat>
Limits of floating point types. C header; see <limits>.
<ciso646>
Empty header because C declarations are incorporated in the C++ language. C header.
<climits>
Limits of integer types. C header; see <limits>.
<clocale>
Locale-specific information. C header; see <locale>.
<cmath>
Mathematical functions. C header.
<complex>
Complex numbers.
<csetjmp>
Non-local goto. C header.
<csignal>
Asynchronous signals. C header.
<cstdarg>
Implementing variadic functions. C header.
<cstddef>
Miscellaneous standard definitions. C header.
<cstdio>
Standard input and output. C header; see <iostream> and related headers.
<cstdlib>
Miscellaneous functions and related declarations. C header.
<cstring>
String-handling functions. C header; see <string>.
<ctime>
Date and time functions and types. C header.
<cwchar>
Wide character functions, including I/O. C header; see <locale>, <iostream> and other I/O-related headers, <string>.
<cwctype>
Wide character classification and case conversion. C header; see <locale>.
<deque>
Deque (double-ended queue) standard container.
<exception>
Base exception class and functions related to exception-handling.
<fstream>
File-based stream I/O.
<functional>
Functional objects, for use with standard algorithms.
<iomanip>
I/O manipulators, for use with standard I/O streams.
<ios>
Base class declarations for all I/O streams.
<iosfwd>
Forward declarations for I/O objects.
<iostream>
Declarations of standard I/O objects.
<istream>
Input streams and input/output streams.
<iterator>
Iterators for working with standard containers and algorithms. See Chapter 11 for more information.
<limits>
Limits of numerical types.
<list>
Standard linked list container.
<locale>
Locale-specific information for formatting and parsing numbers, dates, times, and currency, plus character related functions for classifying, converting, and comparing characters and strings.
<map>
Associative map (sometimes called a dictionary) standard container.
<memory>
Allocators, algorithms for uninitialized memory, and smart pointers (auto_ptr).
<new>
Global new and delete operators and other functions related to managing dynamic memory.
<numeric>
Numerical algorithms.
<ostream>
Output streams.
<queue>
Wueue and priority queue container adapters.
<set>
Associative set container.
<sstream>
String-based I/O streams.
<stack>
Stack container adapter.
<stdexcept>
Standard exception classes.
<streambuf>
Low-level stream buffers, used by high-level I/O streams.
<string>
Strings and wide-character strings.
<strstream>
Deprecated string streams. Not covered in this book; use <sstream> instead.
<typeinfo>
Runtime type information.
<utility>
Miscellaneous templates, such as pair, most often used with standard containers and algorithms.
<valarray>
Numerical arrays.
<vector>
Vector (array-like) standard container.

C library wrappers

The C++ library includes the entire C standard library (from the 1990 C standard, plus Amendment 1), where each C header such as <stdio.h> is wrapped as a C++ header, e.g., <cstdio>. Being part of the C++ standard, all types, functions, and objects are declared in the std namespace.

The external names are also reserved in the global namespace. Types are declared in the global and std namespaces when you #include the corresponding header. That is, ::std::FILE is the same type as ::FILE if you #include <cstdio>.

The C standard permits macros to be defined to mask function names. In the C++ wrappers for these headers, the names must be declared as function, not macros. Thus, for example, the C <stdio.h> header might contain the following:

extern int printf(const char* fmt, ...);
#define printf printf

In C++, the printf macro is not permitted, so the <cstdio> header must declare the printf function in the std namespace, so you can use it as std::printf, for example.

A deprecated feature of C++ is that the C standard headers are also available as their original C names, e.g., <stdio.h>. When used in this fashion, their names are in the global namespace, as though a using declaration were applied to each name, e.g., using std::printf. Because this use of the C headers is deprecated, new code should use the <cstdio>, etc., style C headers.

Traits

Traits are used throughout the C++ library. A trait is a template that describes its template parameters through a series of typedefs and related declarations. At first glance, it seems that traits obscure information, hiding types and other declartions in a morass of templates. This is true, but traits are also powerful tools for use in writing templates.

Character traits

One of the most commonly used traits is char_traits<>, in the <string> header. The standard declares two specializations: char_traits<char> and char_traits<wchar_t>.

The rest of the C++ library use character traits to obtain types and functions for working with characters. For example, the basic_istream class template takes a character type, charT, and a character traits type as template parameters. The default value for the traits parameter is char_traits<charT>, which is the character traits defined in <string>. The basic_istream template declares the get() function, which reads a character and returns its integer equivalent. The return type is obtained from the character traits template, specifically the int_type typedef.

At a basic level, the typical C++ programmer does not need to be concerned with the implementation of traits. Instead, you can use the istream and string classes, and everything just works. If you are curious, you can trace the declaration, say, of istream::int_type as follows:

istream::int_type => basic_istream<char>::int_type => traits::int_type => char_traits<char>::int_type => int

As you can see, traits can be difficult to follow when you need to know the exact type of one of the types declared in a standard container.

Once you get used to them, though, you can see how valuable traits can be. Consider what happens when you change from istream::int_type to wistream::int_type:

wistream::int_type => basic_istream<wchar_t>::int_type => traits::int_type => char_traits<wchar_t>::int_type => wint_t

Notice that the declarations of basic_istream and the other templates do not differ when the template parameter changes from char to wchar_t. Instead, you end up with a different template specialization for char_traits<>, which ends up directing you to a different integer type.

The biggest benefit of traits, therefore, is to the author of a class or library.

Iterator traits

Traits are also useful for iterators. They permit the use of plain pointers as iterators. An algorithm often needs to know the iterator category, to provide specializations that optmize performance for, say, random access iterators. Traits provide a standard way to convey this information to the algorithm, namely, using the iterator_category typedef.

For example, the distance function returns the distance between two iterators. For random access iterators, the distance can be computed by subtraction. For other iterators, the distance must be computed by incrementing an iterator and counting the number of increments needed. Example 9-1 shows a simple implementation of distance that uses the iterator traits to choose the optimized random access implementation or the slower implementation for all other input iterators.

Example 9-1: Implementing the distance function template.

// Helper function, overloaded for random access iterators.
template<typename InputIter>
typename std::iterator_traits<InputIter>::difference_type
compute_dist(InputIter first, InputIter last,
             const std::random_access_iterator_tag&)
{
  return last - first;
}

// Helper function, overloaded for all other input
// iterators.
template<typename InputIter>
typename std::iterator_traits<InputIter>::difference_type
compute_dist(InputIter first, InputIter last,
             const std::input_iterator_tag&)
{
  typename std::iterator_traits<InputIter>::difference_type
    count = 0;
  while (first != last) {
    ++first;
    ++count;
  }
  return count;
}

// Main distance function, which calls the helper function,
// using the iterator tag to differentiate the overloaded
// functions.
template<typename InputIter>
typename std::iterator_traits<InputIter>::difference_type
distance(InputIter first, InputIter last)
{
  return compute_dist(first, last,
    std::iterator_traits<InputIter>::iterator_category());
}

Being able to optimize algorithms for certain kinds of iterators is one benefit of using traits, but the real power comes from the iterator_traits<T*> specialization. This class permits the use of any pointer type as an iterator.

As mentioned earlier, algorithms use the iterator traits to determine the iterator category, the type of values stored by the iterator, and so on. All of this information could be obtained from any iterator class without the need for traits. Plain pointer types, however, do not have the capability of declaring nested types. Thus, traits are the only way to provide this information for plain pointers.

Consider how the distance function is called in the following example:

int data[] = { 10, 42, 69, 13, 100, -1 };
distance(&data[1], &data[4]);

The compiler infers the InputIter template parameter as type int*. The iterator_traits<T*> template is expanded (see the details in the <iterator> section of Chapter 13) to obtain the iterator_category type (random_access_iterator_tag) and difference_type (ptrdiff_t).

Custom traits

Traits can be useful any time you are using templates. You never know what the template parameters might be. Sometimes, you want to specialize your own code according to a template parameter.

For example, all the standard sequence containers have a constructor that takes two iterators as arguments, as shown below:

template<typename InputIterator>
list(InputIterator first, InputIterator last);

But take a closer look at the declaration. The author's intent is clear: that the template parameter must be an input iterator, but nothing in the declaration enforces this restriction. The compiler allows any type to be used (at least any types that can be copied).

If the InputIterator type actually is an input iterator, the list is constructed by copying all the elements in the range [first, last). But if the InputIterator type is an integral type, the first argument is interpereted as a count, and the last argument is interpreted as an integer value, which is converted to the the value type of the container; the container is then initialized with first copies of the last value, which is ordinarily the work of a different constructor. (See Chapter 11 for more information about these constructors.)

If you need to implement your own container template, you must find a way to implement this kind of constructor. The simplest way is to define a traits template that can tell you whether a type is an integral type. Example 9-2 shows one possible implementation, and how it can be used by a container.

Example 9-2: Differentiating integral types using traits.

// Type trait to test whether a type is an integer.
struct is_integer_tag {};
struct is_not_integer_tag {};

// The default is that a type is not an integral type.
template<typename T>
struct is_integer {
  typedef is_not_integer_tag tag;
};

// Override the default explicitly for all integral types.
template<>
struct is_integer<int> {
  typedef is_integer_tag tag;
};
template<>
struct is_integer<short> {
  typedef is_integer_tag tag;
};
template<>
struct is_integer<unsigned> {
  typedef is_integer_tag tag;
};
// and so on for char, signed char, short, etc.

// Constructor uses the is_integer trait to distinguish
// integral from non-integral types and dispatches
// to the correct overloaded construct function.
template<typename T, typename A>
template<typename InputIter>
list<T,A>::list(InputIter first, InputIter last)
{
  construct(first, last, is_integer<InputIter>::tag());
}

// The construct member functions finish the initialization
// of the list. The integral version casts the arguments
// to the size and value types.
template<typename T, typename A>
template<typename InputIter>
void list<T,A>::construct(InputIter first, InputIter last,
                          is_integer_tag)
{
  insert(begin(), static_cast<size_type>(first),
                  static_cast<T>(last));
}

// The non-integral version copies elements from the
// iterator range.
template<typename T, typename A>
template<typename InputIter>
void list<T,A>::construct(InputIter first, InputIter last,
                          is_not_integer_tag)
{
  insert(begin(), first, last);
}

Traits can be used to characterize any type and specialize templates for a wide variety of situations. See the Boost and Loki projects (described in Appendix ###) for other definitions and uses of traits.

Allocators

You know about the new and delete expressions for allocating and freeing dynamic memory. They are simple, expressive, and useful, but the standard library does not necessarily use them internally. Instead, the standard library uses an abstraction called allocators.

The <memory> section of Chapter 13 describes the allocator class template in detail. This section describes the general principles of allocators and how they are used.

Using allocators

An allocator is a simple object that manages dynamic memory. A trivial allocator might be implemented on top of new and delete. Other allocators might manage memory that is shared between processes, or differentiate between different kinds of pointers (such as near and far pointers found on old PCs).

Every class that must manage memory internally, such as every container class template, takes an allocator template parameter. The default value of the template parameter is the std::allocator template, so you rarely have to concern yourself with allocators. The only time you must is when you define a custom allocator or write your own container.

If you write a new container class template, be sure to take an allocator parameter, the same way the standard containers do. Use the allocator to manage internal memory for your container. See Chapter 10 for more information about containers and how they use allocators.

Custom allocators

Some operating systems permit the use of multiple, separate heaps, or pools of dynamic memory. You might think that you could use separate allocator objects for separate heaps, where each object allocates memory from its heap and returns free memory to its heap. If so, you would be wrong.

In the interest of performance optimization, the standard requires that all objects of a given allocator class be equivalent. That is, an allocator object cannot keep any state, such as a handle for a private heap. Instead, all of your allocator objects must share a single heap. You can define multiple classes, where each class gets its own heap, but that's quite different from having each object maintain its own heap.

In spite of the restrictions, there are uses for custom allocators.

Numerics

The C++ library has several headers to support numerical programming. The most basic header is <cmath>, which declares a number of transcendental and other mathematical functions. This C header is expanded in C++ to declare overloaded versions of every function. For example, where C declares only exp(double), C++ declares exp(float) and exp(long double), too.

The <complex> header declares a class template for complex numbers, with the specializations you probably expect for float, double, and long double. Transcendental and I/O functions are also declared for the complex numbers.

The <numeric> header declares a few algorithms for numerical sequences.

The most interesting numerical functions are in <valarray>. A valarray is like an ordinary numerical array, but the compiler is free to make some simplifying assumptions to improve optimization.

Use the complex template as an example of how to define custom numeric types. For example, suppose you want to define a type to represent rational numbers (fractions). In order to use rational objects in a valarray, you must define the class so it behaves the same as ordinary values, such as ints. In other words, a custom numeric type should have the following:

Constructing an object and assigning to the object should have the same result.

When overloading arithmetic and comparison operators, think about which operators are meaningful (e.g., most arithmetic types should have addition, subtraction, multiplication, and division, but only integral types will probably have remainder, bitwise, and shift operators). You will probably want to provide overloaded operators that accept the builtin types as operands. For example, the complex template defines the following functions for operator+:

template<typename T>
  complex<T> operator+(const complex<T>& z);

template<typename T> complex<T>
  operator+(const complex<T>& x, const complex<T>& y);
template<typename T> complex<T>
  operator+(const complex<T>& x, const T& y);
template<typename T> complex<T>
  operator+(const T& x, const complex<T>& y);

Example 9-3 shows excepts from a simple rational class template.

Example 9-3: The rational class template for rational numbers.

template<typename T>
class rational
{
public:
  typedef T value_type;

  rational()                 : num_(0),   den_(1) {}
  rational(value_type num)   : num_(num), den_(1) {}
  rational(value_type num, value_type den)
    : num_(num), den_(den) { reduce(); }
  rational(const rational& r): num_(r.num_), den_(r.den_) {}
  template<typename U>
  rational(const rational<U>& r)
    : num_(r.num_), den_(r.den_) { reduce(); }

  rational& operator=(const rational& r)
    { num_ = r.num_; den_ = r.den_; return *this; }
  template<typename U>
  rational& operator=(const rational<U>& r)
    { assign(r.numerator(), r.denominator()); return *this; }

  void assign(value_type n, value_type d)
    { num_ = n; den_ = d; reduce(); }

  value_type numerator()   const { return num_; }
  value_type denominator() const { return den_; }

private:
  void reduce();
  value_type num_;
  value_type den_;
};

// Reduce the numerator and denominator by the gcd.
// Ensure the denominator is nonnegative.
template<typename T>
void rational<T>::reduce()
{
  if (den_ < 0) {
    den_ = -den_;
    num_ = -num_;
  }
  T d = gcd(num_, den_);
  num_ /= d;
  den_ /= d;
}

// Greatest common divisor using Euclid's algorithm.
template<typename T>
T gcd(T n, T d)
{
  n = abs(n);
  while (d != 0) {
    T t = n % d;
    n = d;
    d = t;
  }
  return n;
}

// Multiplication. Other operators are similar.
// Multiplication assignment operator. Often implemented
// as a member function, but there is no need to do so.
template<typename T, typename U>
rational<T>& operator*=(rational<T>& dst,
                        const rational<U>& src)
{
  dst.assign(dst.numerator() * src.numerator(),
             dst.denominator() * src.denominator());
  return dst;
}

// Multiply two rational numbers.
template<typename T>
rational<T> operator*(const rational<T>& a,
                      const rational<T>& b)
{
  rational<T> result(a);
  result *= b;
  return result;
}

// Multiply rational times an integral value.
template<typename T>
rational<T> operator*(const T& a, const rational<T>& b)
{
  return rational<T>(a * b.numerator(), b.denominator());
}

template<typename T>
rational<T> operator*(const rational<T>& a, const T& b)
{
  return rational<T>(b * a.numerator(), a.denominator());
}

// Comparison. All other comparisons can be implemented
// in terms of operator== and operator<.
template<typename T>
bool operator==(const rational<T>& a, const rational<T>& b)
{
  // Precondition: both operands are reduced.
  return a.numerator() == b.numerator() &&
         a.denominator() == b.denominator();
}

template<typename T>
bool operator<(const rational<T>& a, const rational<T>& b)
{
  return a.numerator() * b.denominator() <
         b.numerator() * a.denominator();
}

Many numerical programmers find the C++ standard library to be lacking. The Blitz++ project is a popular high-performance numerical library. Boost also has some numerical headers, such as a full-featured rational clas template. See Appendix ### for information about these and other C++ libraries.