343. Unspecified library header dependencies

Section: 20 [library] Status: Resolved Submitter: Martin Sebor Opened: 2001-10-09 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [library].

View all issues with Resolved status.

Discussion:

The synopses of the C++ library headers clearly show which names are required to be defined in each header. Since in order to implement the classes and templates defined in these headers declarations of other templates (but not necessarily their definitions) are typically necessary the standard in 17.4.4, p1 permits library implementers to include any headers needed to implement the definitions in each header.

For instance, although it is not explicitly specified in the synopsis of <string>, at the point of definition of the std::basic_string template the declaration of the std::allocator template must be in scope. All current implementations simply include <memory> from within <string>, either directly or indirectly, to bring the declaration of std::allocator into scope.

Additionally, however, some implementation also include <istream> and <ostream> at the top of <string> to bring the declarations of std::basic_istream and std::basic_ostream into scope (which are needed in order to implement the string inserter and extractor operators (21.3.7.9 [lib.string.io])). Other implementations only include <iosfwd>, since strictly speaking, only the declarations and not the full definitions are necessary.

Obviously, it is possible to implement <string> without actually providing the full definitions of all the templates std::basic_string uses (std::allocator, std::basic_istream, and std::basic_ostream). Furthermore, not only is it possible, doing so is likely to have a positive effect on compile-time efficiency.

But while it may seem perfectly reasonable to expect a program that uses the std::basic_string insertion and extraction operators to also explicitly include <istream> or <ostream>, respectively, it doesn't seem reasonable to also expect it to explicitly include <memory>. Since what's reasonable and what isn't is highly subjective one would expect the standard to specify what can and what cannot be assumed. Unfortunately, that isn't the case.

The examples below demonstrate the issue.

Example 1:

It is not clear whether the following program is complete:

#include <string>

extern std::basic_ostream<char> &strm;

int main () {
    strm << std::string ("Hello, World!\n");
}

or whether one must explicitly include <memory> or <ostream> (or both) in addition to <string> in order for the program to compile.

Example 2:

Similarly, it is unclear whether the following program is complete:

#include <istream>

extern std::basic_iostream<char> &strm;

int main () {
    strm << "Hello, World!\n";
}

or whether one needs to explicitly include <ostream>, and perhaps even other headers containing the definitions of other required templates:

#include <ios>
#include <istream>
#include <ostream>
#include <streambuf>

extern std::basic_iostream<char> &strm;

int main () {
    strm << "Hello, World!\n";
}

Example 3:

Likewise, it seems unclear whether the program below is complete:

#include <iterator>

bool foo (std::istream_iterator<int> a, std::istream_iterator<int> b)
{
    return a == b;
}

int main () { }

or whether one should be required to include <istream>.

There are many more examples that demonstrate this lack of a requirement. I believe that in a good number of cases it would be unreasonable to require that a program explicitly include all the headers necessary for a particular template to be specialized, but I think that there are cases such as some of those above where it would be desirable to allow implementations to include only as much as necessary and not more.

[ post Bellevue: ]

Position taken in prior reviews is that the idea of a table of header dependencies is a good one. Our view is that a full paper is needed to do justice to this, and we've made that recommendation to the issue author.

[ 2009-07 Frankfurt ]

Resolved. Handled by LWG 1178.

Proposed resolution:

For every C++ library header, supply a minimum set of other C++ library headers that are required to be included by that header. The proposed list is below (C++ headers for C Library Facilities, table 12 in 17.4.1.2, p3, are omitted):

+------------+--------------------+
| C++ header |required to include |
+============+====================+
|<algorithm> |                    |
+------------+--------------------+
|<bitset>    |                    |
+------------+--------------------+
|<complex>   |                    |
+------------+--------------------+
|<deque>     |<memory>            |
+------------+--------------------+
|<exception> |                    |
+------------+--------------------+
|<fstream>   |<ios>               |
+------------+--------------------+
|<functional>|                    |
+------------+--------------------+
|<iomanip>   |<ios>               |
+------------+--------------------+
|<ios>       |<streambuf>         |
+------------+--------------------+
|<iosfwd>    |                    |
+------------+--------------------+
|<iostream>  |<istream>, <ostream>|
+------------+--------------------+
|<istream>   |<ios>               |
+------------+--------------------+
|<iterator>  |                    |
+------------+--------------------+
|<limits>    |                    |
+------------+--------------------+
|<list>      |<memory>            |
+------------+--------------------+
|<locale>    |                    |
+------------+--------------------+
|<map>       |<memory>            |
+------------+--------------------+
|<memory>    |                    |
+------------+--------------------+
|<new>       |<exception>         |
+------------+--------------------+
|<numeric>   |                    |
+------------+--------------------+
|<ostream>   |<ios>               |
+------------+--------------------+
|<queue>     |<deque>             |
+------------+--------------------+
|<set>       |<memory>            |
+------------+--------------------+
|<sstream>   |<ios>, <string>     |
+------------+--------------------+
|<stack>     |<deque>             |
+------------+--------------------+
|<stdexcept> |                    |
+------------+--------------------+
|<streambuf> |<ios>               |
+------------+--------------------+
|<string>    |<memory>            |
+------------+--------------------+
|<strstream> |                    |
+------------+--------------------+
|<typeinfo>  |<exception>         |
+------------+--------------------+
|<utility>   |                    |
+------------+--------------------+
|<valarray>  |                    |
+------------+--------------------+
|<vector>    |<memory>            |
+------------+--------------------+

Rationale:

The portability problem is real. A program that works correctly on one implementation might fail on another, because of different header dependencies. This problem was understood before the standard was completed, and it was a conscious design choice.

One possible way to deal with this, as a library extension, would be an <all> header.

Hinnant: It's time we dealt with this issue for C++0X. Reopened.