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

时间:2019-03-12 09:37:35

标签: 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;
}

输入:

1
2
11
13

输出:

1
2
exception
// Printing Empty string

预期输出:

1
2
exception
exception

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

1 个答案:

答案 0 :(得分:42)

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

  

[class.copy.elision] (强调我的意思)

     

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

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

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

     
      
  • 如果throw-expression的操作数是非易失性自动对象的名称(函数或catch子句参数除外)   其作用域没有扩展到最里面的try-block的末尾(如果有的话)
  •   

这是一系列优化,可以避免或尽可能有效地完成异常对象的复制初始化。现在,std::string移动构造的常见实现是将源字符串保留为空。这似乎正是您的代码发生的情况。外部作用域中的temp从(并留为空白)移动。

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

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

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