【问题标题】:Would compiler optimization remove try/catch block if catch does nothing?如果 catch 什么都不做,编译器优化会删除 try/catch 块吗?
【发布时间】:2023-03-16 03:17:02
【问题描述】:

我正在使用包含很多 try catch 块的代码,但大多数情况下 catch 块什么都不做。如下面的代码中fib 函数正在抛出invalid_argument 异常。 main 中的函数调用在 try 块中,但 catch 块除了捕获异常之外什么都不做。

我想知道编译器是否会在代码优化期间去掉这种异常处理?

#include <iostream>
#include <exception>


// Declaration for Wmissing-declarations flag
int fib(int);

int fib(int n)
{
    if (n < 0)
        {
            throw std::invalid_argument("Invalid argument");
        }
    if (n == 0 || n == 1)
        return n;
    
    return fib(n-1) + fib(n-2);
}

int main(int argc, char *argv[])
{
    int _number;
    std::cin >> _number;
    try
    {
        std::cout << fib(_number) << std::endl;
    }
    catch(const std::invalid_argument & e)
    {
        
    }
    return 0;
}

在打开大多数(我所知道的)标志的情况下编译上面的代码,如下所示,不会显示任何警告。

g++ -o except exceptions.cxx -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused

【问题讨论】:

  • 存在捕获这一事实确保了异常被捕获。否则它将立即终止此功能以及可能许多其他功能,并且很可能会导致程序终止。它最终会导致程序返回非零值 - 用户外部的错误代码信息,表明程序中发生错误。但是,这些被捕获并返回 0。
  • 编译器无法删除catch 作为优化,因为这会改变程序的行为。除了极少数和特定的情况外,优化永远不允许改变可观察的行为。它也不能删除throw,因为如果n &lt; 0,函数fib不能被允许return
  • 一个完美的编译器可以被允许“重写”你的程序以在不使用throw std::invalid_argument("Invalid argument");的情况下具有相同的可观察行为,但这对你来说一定是察觉不到的(就行为而言)。所以真正的问题是为什么你特别关心异常是否被优化?你怎么知道区别?这可能是 XY 问题。
  • 编译器至少必须展开堆栈。鉴于fib 在必须展开的递归调用中有两个临时变量,并且编译器不知道fib 将从什么堆栈深度抛出,仅此一项就排除了删除。 godbolt.org/z/v37n6rxba
  • 问题的前提是错误的:一个空的try...catch块不会什么都不做。

标签: c++ performance c++11 error-handling c++20


【解决方案1】:

我想知道编译器是否会在代码优化期间去掉这种异常处理?

TL;DR:不,编译器不能并且不得优化掉此类异常处理。


作为mentioned in the comments catch与没有try ... catch 块相同。

在您的示例中,虽然catch 块中实际上没有执行 代码,但将负数传递给fib 函数时抛出的异常仍然被那个块。那时,catch在概念上被输入,然后很快退出——将控制权(无声地)传递给紧随该空块之后的代码。

在调用catch 块时,还会执行一些(necessary) stack unwinding 和可能的其他“补救”操作;因此,即使是这样一个空的 catch 块,实际上仍然会捕获相关异常(指定为其参数)。

因此,对于您的代码(原样),输入测试值-3 将导致程序正常、成功终止。在我的 Windows 控制台(从 Visual Studio 调用)上,我看到了:

-3

C:\SGGCode.exe (process 2888) exited with code 0.
Press any key to close this window . . .

但是,如果我删除 try..catch 块,提供以下 main(其他所有内容保持不变):

int main(int argc, char* argv[])
{
    int _number;
    std::cin >> _number;
    std::cout << fib(_number) << std::endl; // Removed try...catch
    return 0;
}

然后,当我运行程序并给出相同的输入时,我得到了这个未捕获的异常错误:

-3

C:\SGGCode.exe (process 2420) exited with code -1073740791.
Press any key to close this window . . .

(注意-10737407910xc00004096,也就是uncaught exception error。)


为了使事情更清楚,请尝试在您的main 中的return 0; 行之前添加如下一行:

    std::cout << "See - I'm still here!" << std::endl;

使用空的catch,将执行;没有它,程序会在到达之前崩溃。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-05-27
    相关资源
    最近更新 更多