一个怪异的C++函数定义方式
文章目录
在阅读clickhouse代码的过程中,发现有一种函数定义形式很怪异,一度以为是代码写错了。。。
int main()
try
{
XXX
}
catch (DB::Exception & e)
{
XXX
}
形式如上,函数名之后怎么能不加花括号呢?这是不是不符合语法?深入了解之后发现原来是我孤陋寡闻了。
https://en.cppreference.com/w/cpp/language/function-try-block
上面的代码其实跟下面差不多
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
文章作者 后端侠
上次更新 2021-09-21