GotW #34

Home Blog Talks Books & Articles Training & Consulting

On the
blog
RSS feed November 4: Other Concurrency Sessions at PDC
November 3
: PDC'09: Tutorial & Panel
October 26: Hoare on Testing
October 23
: Deprecating export Considered for ISO C++0x

This is the original GotW problem and solution substantially as posted to Usenet. See the book More Exceptional C++ (Addison-Wesley, 2002) for the most current solution to this GotW issue. The solutions in the book have been revised and expanded since their initial appearance in GotW. The book versions also incorporate corrections, new material, and conformance to the final ANSI/ISO C++ standard.

Forward Declarations
Difficulty: 8 / 10

Forward declarations are a great way to eliminate needless compile-time dependencies. But here's an example of a forward-declaration snare... how would you avoid it?

Problem

JG Question

1. Forward declarations are very useful tools. In this case, they don't work as the programmer expected. Why are the marked lines errors?

    // file f.h
    #ifndef XXX_F_H_
    #define XXX_F_H_

    class ostream;  // error
    class string;   // error

    string f( const ostream& );

    #endif

Guru Question

2. Without including any other files, can you write the correct forward declarations for ostream and string above?

Solution

1. Forward declarations are very useful tools. In this case, they don't work as the programmer expected. Why are the marked lines errors?

    // file f.h
    #ifndef XXX_F_H_
    #define XXX_F_H_

    class ostream;  // error
    class string;   // error

    string f( const ostream& );

    #endif

Alas, you cannot forward-declare ostream and string this way because they are not classes... both are typedefs of templates.

(True, you used to be able to forward-declare ostream and string this way, but that was many years ago and is no longer possible in Standard C++.)

2. Without including any other files, can you write the correct forward declarations for ostream and string above?

Unfortunately, the answer is that there is no standard and portable way to do this. The standard says:

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified.

Among other things, this allows vendors to provide implementations of the standard library that have more template parameters for library templates than the standard requires (suitably defaulted, of course, to remain compatible).

The best you can do (which is not a solution to the problem "without including any other files") is this:

    #include <iosfwd>
    #include <string>

The iosfwd header does contain bona fide forward declarations. The string header does not. This is all that you can do and still be portable. Fortunately, forward-declaring string and ostream isn't too much of an issue in practice since they're generally small and widely-used. The same is true for most standard headers. However, beware the pitfalls, and don't be tempted to start forward-declaring templates -- or anything else -- that belongs to namespace std... that's reserved for the compiler and library writers, and them alone.

Copyright 2009 Herb Sutter