#include <fstream>
#include <iostream>
#include <map>
int main(int argc, char** argv) {
try {
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
std::cout << m.at("c") << std::endl;
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
在C ++中,当检索地图的不存在的键时,异常看起来像map::at: key not found
。未提供有关键的信息。
此外,如果正在访问不存在的文件,则std::ios_base::failure
的异常消息看起来像ios_base::clear: unspecified iostream_category error
。未提供导致异常的文件名。因此,如果项目中有许多map.at()
或ifstream is
用途,可能需要花费很长时间才能找出异常的来源。
与此相反,Python可能会告诉您KeyError: 'c'
或FileNotFoundError: [Errno 2] No such file or directory: 'foo'
。
这只是一个C ++约定吗?谢谢。
答案 0 :(得分:37)
问题是C ++对象模型,它与Python不同。相比之下,首先让我们回答一下:Python在异常对象中存储什么来打印密钥?它是保持该对象存活的参考。
这不能简单地在C ++中完成。
std::out_of_range
无法按原样存储指针或引用。异常的处理程序可能在远处的块中。这意味着在输入处理程序之前,键很可能超出范围。如果引用了密钥,我们会得到未定义的行为。
std::out_of_range
是一个具体的类。不是像std::map
这样的模板。它不能轻易地将密钥复制到自身中。有许多不同的Key
类型,显然无法解释所有这些类型。即使它可以,如果复制密钥非常昂贵怎么办?甚至是不可复制的?即使在这种情况不正确的情况下,如果密钥不能转换为字符串或可打印怎么办?
以上几点并不意味着它是不可能的。 std::map::at
可以抛出一个类型擦除的std::out_of_range
子类,依此类推。但我希望你看到它有非平凡的开销。 C ++完全不是为您不需要或使用的功能付费。让每个人无条件地承担这种开销并不符合这种设计。
答案 1 :(得分:3)
C ++标准指定map::at(const key_type& k)
将启动std::out_of_range
异常(如果值不在其中);仅此而已...但不能少; map::at()
的一个特定实现,但问题是k的key_type必须转换为char *。
所以有几种选择:
std::map
不存储没有(至少是隐式)转换为char *的类型:但是有一个要求应用于key_store类型并非严格必要std::out_of_range
例外中提供不同的消息:但此解决方案并不总是显示预期的信息从另一个角度来看,std::out_of_range
继承自std::logic_error
; C ++标准区分了两种主要类型的异常:
在我们的例子中,我们可以很容易地检查一个元素的存在(所以我们的案例修复了这个案例)
答案 2 :(得分:0)
With C++, it is possible to create a personalized exception inheriting from std::exception with extra info, like filename. Here is an example :
#include <sstream>
class MyException : public std::exception {
public :
MyException (std::exception e, std::string filename = "") : std::exception (e), str_ (filename) {}
MyException (const MyException& e) : std::exception (e), str_ (e.str_) {}
const char* what () const throw () {
std::ostringstream oss;
oss << "exception occured in " << str_ << " : ";
oss << std::exception::what ();
return oss.str ().c_str ();
}
std::string str_;
};
int main(int argc, char** argv) {
try {
try {
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
std::cout << m.at("c") << std::endl;
}
catch (const std::exception& e) {
MyException f (e, "main");
throw (f);
}
}
catch (const MyException& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
will display :
exception occured in main : std::exception