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>它时,它会编译,并且工作正常。
除非明确指定,否则标准标头是否包含另一个标头是实现细节。
使用模板会涉及更多一些,但只是为了向您指出某个方向,请考虑这个玩具示例:
// 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>沿线的某个地方。但这可能会随着编译器版本或不同编译器之间的变化而变化。