为什么地图不包括 out_of_range?

Cre*_*psy 11 c++ language-lawyer

考虑以下无法编译的代码:

#include <map>

//#include <stdexcept>  // uncommenting this works

int main() {

    std::map<int, int> test;
    try {
        test.at(10);
    } catch(std::out_of_range& e) {
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么不std::map包括std::out_of_range,即使它使用它?(.at()可以扔一个std::out_of_range)。

当我也包含<stdexcept>它时,它会编译,并且工作正常。

for*_*818 4

除非明确指定,否则标准标头是否包含另一个标头是实现细节。

使用模板会涉及更多一些,但只是为了向您指出某个方向,请考虑这个玩具示例:

// header: my_map.h
struct my_map {
    int at(int);
};
Run Code Online (Sandbox Code Playgroud)

仅在源文件中必须包含异常的标头:

// source: my_map.cpp
#include <stdexcept>
int my_map::at(int) {
    throw std::out_of_range("bla");
}
Run Code Online (Sandbox Code Playgroud)

std::map当然看起来不同,但它也可能隐藏标题中的异常。


标头不包含 std::pair 的 decl 也可以吗

指定要包含的头文件<map><compare>(Since C++20) 和<initializer_list>(Since C++11)。而已。

<map>可能包含其他标头,这是评论中提到的“实现细节”之一。标准中明确允许标准标头包含其他标头的部分是[todo.put引用此处]。

避免这种麻烦的简单经验法则是:包括您使用的内容。


但抛出 std::out_of_range 并不是“实现细节”——它是 std::map 规范的一部分!

考虑此代码使用gcc 10.2进行编译:

#include <map>

int main() {
    std::map<int, int> test;
    try {
        test.at(10);
    } catch(...) {
        return 1;
    }    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

抛出异常out_of_range并捕获,返回1。我们知道什么at可能抛出并且catch(...)会捕获它,但不需要包含异常。

另一方面,同一个编译器会拒绝您的 example,但当我们添加看似不相关的标头时会编译它:

#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;
}
Run Code Online (Sandbox Code Playgroud)

然而,这只是巧合。显然<sstream>确实包括<stdexcept>沿线的某个地方。但这可能会随着编译器版本或不同编译器之间的变化而变化。