为什么Try-Catch块会影响封闭范围内的变量?

Adi*_*han 41 c++ try-catch

为什么temp在捕获到第一个异常后外部变成空的?

#include <iostream>
int main()
{
    std::string temp("exception");
    int value;
    while(std::cin>> value && value != 0)
    {
         try{
              if(value > 9) throw temp;
              else std::cout << value << "\n";
            }
         catch(std::string temp)
         {
              std::cout << temp << "\n";
         }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输入:

1
2
11
13
Run Code Online (Sandbox Code Playgroud)

输出:

1
2
exception
// Printing Empty string
Run Code Online (Sandbox Code Playgroud)

预期产量:

1
2
exception
exception
Run Code Online (Sandbox Code Playgroud)

我使用g ++ 7.3.0编译代码。

Sto*_*ica 41

这似乎是GCC实施复制省略的错误。C ++标准说明以下内容:

[class.copy.elision](重点是我的)

在以下情况下允许复制/移动操作的这种简化,称为复制简化(可以合并以消除多个副本):

  • 在throw表达式中,当操作数是非易失性自动对象(函数或catch子句参数除外)的名称时,其范围不会超出最里面的try块的末尾(如果有) ),可以通过将自动对象直接构造到异常对象中来省略从操作数到异常对象的复制/移动操作

在以下复制初始化上下文中,可以使用移动操作代替复制操作:

  • 如果throw-expression的操作数是非易失性自动对象(函数或catch子句参数除外)的名称, 其范围不会超出最里面的try块的末尾(如果有)

这是一系列优化,可以避免或尽可能有效地避免异常对象的副本初始化。现在,std::string移动构造的常见实现是将源字符串保留为空。这似乎正是您的代码发生的情况。的temp在外部范围从移动(和左空)。

但这不是预期的行为。temp您所抛出的范围超出了(到目前为止)它所抛出的try块。因此,GCC没有业务对其应用复制省略。

一个可能的解决方法是将声明temp放在while循环内。这在std::string每次迭代时都会初始化一个新对象,因此即使GCC移开它,也不会引起注意。

注释中提到了另一个解决方法,该解决方法是使外部temp对象成为const对象。这将强制执行复制操作(因为移动操作需要非常量源对象)。