在阅读clickhouse代码的过程中,发现有一种函数定义形式很怪异,一度以为是代码写错了。。
int main() try { XXX } catch (DB::Exception & e) { XXX }
形式如上,函数名之后怎么能不加花括号呢?这是不是不符合语法?深入了解之后发现原来是我孤陋寡闻了。
上面的代码其实跟下面差不多
int main() { try { xxx } catch (DB::Exception & e) { xxx } }
第一种看起来比较怪异的形式在cpp里叫做function-try-block, 第二种就是我们常见的function-body
那么function-try-block主要使用于哪种场合呢?它是不是能捕获到所有的异常呢?让我们看下文档
The primary purpose of function-try-blocks is to respond to an exception thrown from the member initializer list in a constructor by logging and rethrowing, modifying the exception object and rethrowing, throwing a different exception instead, or terminating the program. They are rarely used with destructors or with regular functions. Function-try-block does not catch the exceptions thrown by the copy/move constructors and the destructors of the function parameters passed by value: those exceptions are thrown in context of the caller. Likewise, function-try-block of the main() function does not catch the exceptions thrown from the constructors and destructors of static objects (except for the constructors of function-local statics).
function-try-block这种形式主要用于类构造函数和main函数中,而很少使用在类析构函数和其他常规函数中。
- 在类的构造函数中,function-try-block能够捕获到初始化列表中的异常,用户可以在catch block中打印日志、修改异常、重抛异常。需要注意的是,function-try-block无法捕获传值参数的复制/移动构造函数/析构函数中抛出的异常,这些异常会传递到function-try-block调用者的上下文中。
- 在main函数中,function-try-block能够捕获try块中的异常,但是无法捕获静态对象构造和析构过程中抛出的异常
相关实例如下所示:
#include <exception> #include <iostream> class MyException : public std::exception { public: MyException(const char* msg) : msg(msg) { } virtual const char* what() const throw() { return msg.c_str(); } private: std::string msg; }; class M { public: explicit M(size_t size) { throw MyException("exception from constructor"); } M(const M & other) { throw MyException("exception from copy constructor"); } M(M && other) { throw MyException("exception from move constructor"); } ~M() { // throw MyException("exception from destructor"); } }; class A { public: explicit A() try: m1(10), m2(m1), m3(std::move(m2)) { } catch (std::exception & e) { std::cout << "catch exception in A:A: " << e.what() << std::endl; throw; } ~A() = default; private: M m1; M m2; M m3; }; int main() try { A a; } catch (std::exception & e) { std::cout << "catch exception in main: " << e.what() << std::endl; }
执行初始化列表m1(10)
的时候会抛出异常,然后被A::A中的try catch捕捉到,重新抛出到main函数的上下文,接着被main中的try catch捕捉到。因此运行结果为
catch exception in A:A: exception from constructor catch exception in main: exception from constructor