Why does map not include out_of_range?
Whether a standard header does include another header is an implementation detail unless explicitly specified.
With templates its a little more involved, but just to point you in some direction, consider this toy example:
// header: my_map.h
struct my_map {
int at(int);
};
Only in the source file the header for the exception has to be included:
// source: my_map.cpp
#include <stdexcept>
int my_map::at(int) {
throw std::out_of_range("bla");
}
std::map
surely looks different but it may hide the exception from the header as well.
Is it also ok for a header to not include a decl for std::pair
The headers that are specified to be included via <map>
are <compare>
(since C++20) and <initializer_list>
(since C++11). Nothing more.
<map>
may include other headers and thats one of the "implementation details" mentioned in comments. The part of the standard that explicitly allows standard headers to include others is [todo.put reference here].
The simple rule of thumb that avoids such headace is: Include what you use.
but throwing std::out_of_range is not an "implementation detail" - it is part of the specification of std::map!
Consider that this code compiles with gcc 10.2:
#include <map>
int main() {
std::map<int, int> test;
try {
test.at(10);
} catch(...) {
return 1;
}
return 0;
}
The out_of_range
exception is thrown and catched, and 1 is returned. We know what at
may throw and catch(...)
will catch it, yet no include is needed for the exception.
On the other hand, the same compiler rejects your example, but compiles it when we add a seemingly unrelated header:
#include <map>
#include <sstream> // why this?
int main() {
std::map<int, int> test;
try {
test.at(10);
} catch(std::out_of_range& e) {
}
return 0;
}
However, this only works by coincidence. Apparently <sstream>
does include <stdexcept>
somewhere along the line. But this may change with compiler version or between differernt compilers.
The implementation of std::map
doesn't have to be all in the header file. Yes this is a class template, but it is free to use any internal non-template components.
Consequently, the header file doesn't have to mention std::out_of_range
(it may well be hidden inside one of those non-template components) and so doesn't have to have its definition visible anywhere, or behave as if it includes <stdexcept>
. It is explicitly allowed to, but there is no implicit or explicit obligation in the standard for it to do so. So it might, or might not.
In fact g++-9
behaves as if <map>
includes <stdexcept>
, and g++-10
does not, and in both cases they are correct.
Why does std::map not include std::out_of_range
Assuming you are using GCC 10, GCC developers decided to optimize header dependencies in C++ Standard Library code. From Porting to GCC 10:
Header dependency changes
Some C++ Standard Library headers have been changed to no longer include the <stdexcept> header. As such, C++ programs that used components defined in <stdexcept> or <string> without explicitly including the right headers will no longer compile.
Previously components such as std::runtime_error, std::string and std::allocator were implicitly defined after including unrelated headers such as <array> and <optional>. Correct code should include the appropriate headers for the classes being used.
Also from GCC 10 Release Notes:
Reduced header dependencies, leading to faster compilation for some code.