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.