为什么地图不包含 out_of_range?

Why does map not include out_of_range?

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

#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;
}

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

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

std::map的实现不必全部在头文件中。是的,这是一个 class 模板,但可以免费使用任何内部非模板组件。

因此,头文件不必提及 std::out_of_range(它很可能隐藏在那些非模板组件之一中)因此不必在任何地方都可见其定义,或者表现得好像它包含 <stdexcept>。它是明确允许的,但标准中没有隐含或明确的义务要求它这样做。所以它可能会,也可能不会。

事实上 g++-9 的行为就好像 <map> 包含 <stdexcept>,而 g++-10 不包含,并且在这两种情况下它们都是正确的。

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

模板有点复杂,但只是为了给您指明方向,请考虑这个玩具示例:

// header: my_map.h
struct my_map {
    int at(int);
};

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

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

std::map 肯定看起来不同,但它也可能隐藏 header 的异常。


Is it also ok for a header to not include a decl for std::pair

指定通过 <map> 包含的 header 是 <compare> (C++20 起) 和 <initializer_list> (C++11 起) .仅此而已。

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

避免此类头疼的简单经验法则是:包括您使用的内容。


but throwing std::out_of_range is not an "implementation detail" - it is part of the specification of std::map!

考虑到此代码使用 gcc 10.2:

编译
#include <map>

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

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

另一方面,同一个编译器rejects your example, but compiles it当我们添加一个看似无关的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;
}

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

Why does std::map not include std::out_of_range

假设您使用的是 GCC 10,GCC 开发人员决定优化 header C++ 标准库代码中的依赖项。来自 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.

也来自GCC 10 Release Notes

Reduced header dependencies, leading to faster compilation for some code.