Item 54: Familiarize yourself with the standard library, including TR1

The standard for C++ the document defining the language and its library was ratified in 1998. In 2003, a minor "bug-fix" update was issued. The standardization committee continues its work, however, and a "Version 2.0" C++ standard is expected around 2008 or so. The uncertainty regarding that date explains why people usually refer to the next version of C++ as "C++0x" the 200x version of C++.

C++0x will probably include some interesting new language features, but most new C++ functionality will come in the form of additions to the standard library. We already know what some of the new library functionality will be, because it's been specified in a document known as TR1 ("Technical Report 1" from the C++ Library Working Group). The standardization committee reserves the right to modify TR1 functionality before it's officially enshrined in C++0x, but significant changes are unlikely. For all intents and purposes, TR1 heralds the beginning of a new release of C++ what we might call standard C++ 1.1. You can't be an effective C++ programmer without being familiar with TR1 functionality, because that functionality is a boon to virtually every kind of library and application.

Before surveying what's in TR1, it's worth reviewing the major parts of the standard C++ library specified by C++98:

  • The Standard Template Library (STL), including containers (vector, string, map, etc.); iterators; algorithms (find, sort, TRansform, etc.); function objects (less, greater, etc.); and various container and function object adapters (stack, priority_queue, mem_fun, not1, etc.).

  • Iostreams, including support for user-defined buffering, internationalized IO, and the predefined objects cin, cout, cerr, and clog.

  • Support for internationalization, including the ability to have multiple active locales. Types like wchar_t (usually 16 bits/char) and wstring (strings of wchar_ts) facilitate working with Unicode.

  • Support for numeric processing, including templates for complex numbers (complex) and arrays of pure values (valarray).

  • An exception hierarchy, including the base class exception, its derived classes logic_error and runtime_error, and various classes that inherit from those.

  • C89's standard library. Everything in the 1989 C standard library is also in C++.

If any of the above is unfamiliar to you, I suggest you schedule some quality time with your favorite C++ reference to rectify the situation.

TR1 specifies 14 new components (i.e., pieces of library functionality). All are in the std namespace, more precisely, in the nested namespace tr1. The full name of the TR1 component shared_ptr (see below) is thus std::tr1::shared_ptr. In this book, I customarily omit the std:: when discussing components of the standard library, but I always prefix TR1 components with tr1::.

This book shows examples of the following TR1 components:

  • The smart pointers TR1::shared_ptr and tr1::weak_ptr. TR1::shared_ptrs act like built-in pointers, but they keep track of how many tr1::shared_ptrs point to an object. This is known as reference counting. When the last such pointer is destroyed (i.e., when the reference count for an object becomes zero), the object is automatically deleted. This works well in preventing resource leaks in acyclic data structures, but if two or more objects contain tr1::shared_ptrs such that a cycle is formed, the cycle may keep each object's reference count above zero, even when all external pointers to the cycle have been destroyed (i.e., when the group of objects as a whole is unreachable). That's where TR1::weak_ptrs come in. TR1::weak_ptrs are designed to act as cycle-inducing pointers in otherwise acyclic tr1::shared_ptr-based data structures. tr1::weak_ptrs don't participate in reference counting. When the last tr1::shared_ptr to an object is destroyed, the object is deleted, even if tr1::weak_ptrs continue to point there. Such tr1::weak_ptrs are automatically marked as invalid, however.

    tr1::shared_ptr may be the most widely useful component in TR1. I use it many times in this book, including in Item 13, where I explain why it's so important. (The book contains no uses of tr1::weak_ptr, sorry.)

  • tr1::function, which makes it possible to represent any callable entity (i.e., any function or function object) whose signature is consistent with a target signature. If we wanted to make it possible to register callback functions that take an int and return a string, we could do this:

     void registerCallback(std::string func(int));    // param type is a function                                                  // taking an int and                                                  // returning a string 

    The parameter name func is optional, so registerCallback could be declared this way, instead:

     void registerCallback(std::string (int));       // same as above; param                                                 // name is omitted 

    Note here that "std::string (int)" is a function signature.

    tr1::function makes it possible to make registerCallback much more flexible, accepting as its argument any callable entity that takes an int or anything convertible to an int and that returns a string or anything convertible to a string. TR1::function takes as a template parameter its target function signature:

     void registerCallback(std::tr1::function<std::string (int)> func);                                                  // the param "func" will                                                  // take any callable entity                                                  // with a sig consistent                                                  // with "std::string (int)" 

    This kind of flexibility is astonishingly useful, something I do my best to demonstrate in Item 35.

  • tr1::bind, which does everything the STL binders bind1st and bind2nd do, plus much more. Unlike the pre-TR1 binders, tr1::bind works with both const and non-const member functions. Unlike the pre-TR1 binders, TR1::bind works with by-reference parameters. Unlike the pre-TR1 binders, TR1::bind handles function pointers without help, so there's no need to mess with ptr_fun, mem_fun, or mem_fun_ref before calling TR1::bind. Simply put, TR1::bind is a second-generation binding facility that is significantly better than its predecessor. I show an example of its use in Item 35.

I divide the remaining TR1 components into two sets. The first group offers fairly discrete standalone functionality:

  • Hash tables used to implement sets, multisets, maps, and multimaps. Each new container has an interface modeled on that of its pre-TR1 counterpart. The most surprising thing about TR1's hash tables are their names: TR1::unordered_set, tr1::unordered_multiset, tr1::unordered_map, and tr1::unordered_multimap. These names emphasize that, unlike the contents of a set, multiset, map, or multimap, the elements in a TR1 hash-based container are not in any predictable order.

  • Regular expressions, including the ability to do regular expression-based search and replace operations on strings, to iterate through strings from match to match, etc.

  • Tuples, a nifty generalization of the pair template that's already in the standard library. Whereas pair objects can hold only two objects, however, tr1::tuple objects can hold an arbitrary number. Expat Python and Eiffel programmers, rejoice! A little piece of your former homeland is now part of C++.

  • tr1::array, essentially an "STLified" array, i.e., an array supporting member functions like begin and end. The size of a tr1::array is fixed during compilation; the object uses no dynamic memory.

  • tr1::mem_fn, a syntactically uniform way of adapting member function pointers. Just as tr1::bind subsumes and extends the capabilities of C++98's bind1st and bind2nd, tr1::mem_fn subsumes and extends the capabilities of C++98's mem_fun and mem_fun_ref.

  • tr1::reference_wrapper, a facility to make references act a bit more like objects. Among other things, this makes it possible to create containers that act as if they hold references. (In reality, containers can hold only objects or pointers.)

  • Random number generation facilities that are vastly superior to the rand function that C++ inherited from C's standard library.

  • Mathematical special functions, including Laguerre polynomials, Bessel functions, complete elliptic integrals, and many more.

  • C99 compatibility extensions, a collection of functions and templates designed to bring many new C99 library features to C++.

The second set of TR1 components consists of support technology for more sophisticated template programming techniques, including template metaprogramming (seeItem 48):

  • Type traits, a set of traits classes (see Item 47) to provide compile-time information about types. Given a type T, TR1's type traits can reveal whether T is a built-in type, offers a virtual destructor, is an empty class (see Item 39), is implicitly convertible to some other type U, and much more. TR1's type traits can also reveal the proper alignment for a type, a crucial piece of information for programmers writing custom memory allocation functions (see Item 50).

  • tr1::result_of, a template to deduce the return types of function calls. When writing templates, it's often important to be able to refer to the type of object returned from a call to a function (template), but the return type can depend on the function's parameter types in complex ways. TR1::result_of makes referring to function return types easy. TR1::result_of is used in several places in TR1 itself.

Although the capabilities of some pieces of TR1 (notably TR1::bind and TR1::mem_fn) subsume those of some pre-TR1 components, TR1 is a pure addition to the standard library. No TR1 component replaces an existing component, so legacy code written with pre-TR1 constructs continues to be valid.

TR1 itself is just a document.[] To take advantage of the functionality it specifies, you need access to code that implements it. Eventually, that code will come bundled with compilers, but as I write this in 2005, there is a good chance that if you look for TR1 components in your standard library implementations, at least some will be missing. Fortunately, there is someplace else to look: 10 of the 14 components in TR1 are based on libraries freely available from Boost (see Item 55), so that's an excellent resource for TR1-like functionality. I say "TR1-like," because, though much TR1 functionality is based on Boost libraries, there are places where Boost functionality is currently not an exact match for the TR1 specification. It's possible that by the time you read this, Boost not only will have TR1-conformant implementations for the TR1 components that evolved from Boost libraries, it will also offer implementations of the four TR1 components that were not based on Boost work.

[] As I write this in early 2005, the document has not been finalized, and its URL is subject to change. I therefore suggest you consult the Effective C++ TR1 Information Page, That URL will remain stable.

If you'd like to use Boost's TR1-like libraries as a stopgap until compilers ship with their own TR1 implementations, you may want to avail yourself of a namespace trick. All Boost components are in the namespace boost, but TR1 components are supposed to be in std::tr1. You can tell your compilers to treat references to std::tr1 the same as references to boost. This is how:

 namespace std {  namespace tr1 = ::boost;           // namespace std::tr1 is an alias }                                   // for namespace boost 

Technically, this puts you in the realm of undefined behavior, because, as Item 25 explains, you're not allowed to add anything to the std namespace. In practice, you're unlikely to run into any trouble. When your compilers provide their own TR1 implementations, all you'll need to do is eliminate the above namespace alias; code referring to std::tr1 will continue to be valid.

Probably the most important part of TR1 not based on Boost libraries is hash tables, but hash tables have been available for many years from several sources under the names hash_set, hash_multiset, hash_map, and hash_multimap. There is a good chance that the libraries shipping with your compilers already contain these templates. If not, fire up your favorite search engine and search for these names (as well as their TR1 appellations), because you're sure to find several sources for them, both commercial and freeware.

Things to Remember

  • The primary standard C++ library functionality consists of the STL, iostreams, and locales. The C99 standard library is also included.

  • TR1 adds support for smart pointers (e.g., tr1::shared_ptr), generalized function pointers (tr1::function), hash-based containers, regular expressions, and 10 other components.

  • TR1 itself is only a specification. To take advantage of TR1, you need an implementation. One source for implementations of TR1 components is Boost.

Effective C++ Third Edition 55 Specific Ways to Improve Your Programs and Designs
Effective C++ Third Edition 55 Specific Ways to Improve Your Programs and Designs
ISBN: 321334876
Year: 2006
Pages: 102 © 2008-2017.
If you may any questions please contact us: