一个怪异的C++函数定义方式

简介: 一个怪异的C++函数定义方式

在阅读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
相关文章
|
8天前
|
C++
C++一分钟之-函数定义与调用
【6月更文挑战第19天】在C++中,函数是代码复用的关键,它们促进模块化和可读性。了解函数定义(返回类型、函数名、参数列表和函数体)和调用至关重要。示例中展示了如何定义如`add`的函数及如何调用。常见错误包括参数类型不匹配、缺少原型声明、忽略返回值和误解函数重载。通过正确声明、匹配类型、处理返回值和理解重载规则,可以避免这些问题。实战代码示例演示了良好实践。
17 1
|
编译器 C++
C++函数参数传递的三种方式
C++函数参数传递的三种方式
170 0
|
C++ 计算机视觉 数据格式
C/C++主调函数从被调函数中获取(各种类型)数据内容方式的梳理归纳
C/C++主调函数从被调函数中获取(各种类型)数据内容方式的梳理归纳
183 1
C/C++主调函数从被调函数中获取(各种类型)数据内容方式的梳理归纳
|
存储 编译器 文件存储
C++语言中多文件组合方式之经典
C++语言中多文件组合方式之经典
195 0
|
API C语言 C++
C++文件操作的5种方式
C++文件操作的5种方式
138 1
|
存储 人工智能 程序员
C与C++的最常用输入输出方式对比
C与C++的最常用输入输出方式对比,IO,scanf,printf,cin,cout,占位符。使用方法,函数声明,代码实例。区别,优缺点。
255 1
C与C++的最常用输入输出方式对比
|
C++
C++常量定义的两种方式
# C++常量 作用:用于记录程序中不可更改的数据 C++常量定义的两种方式 1.#define 宏常量:#define 常量名 常量值 ​ 通常在文件上方定义,表示一个常量 2. const修饰的变量:const 数据类型 常量名 = 常量值 ​ 通常在变量定义前加关键字const,修饰该变量为常量,不可修改 示例:
184 0
|
存储 人工智能 算法
C++如何处理图的存储方式
C++如何处理图的存储方式
215 0
C++如何处理图的存储方式
|
编译器 C++ 容器
C++中多用引用传递方式替换值传递方式
C++中多用引用传递方式替换值传递方式
427 0