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

问题描述 投票:42回答:2

为什么外部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编译我的代码。

c++ try-catch
2个回答
42
投票

这似乎是GCC实施复制省略的一个错误。 C ++标准说明如下:

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

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

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

在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:

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

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

但这不是预期的行为。你抛出的temp的范围超过(到目前为止)它所引入的try块。所以GCC没有业务应用复制省略。

一个可能的解决方法是将temp的声明放在while循环中。这会在每次迭代时初始化一个新的std::string对象,所以即使GCC从它移动,它也不会引人注意。

注释中提到了另一种解决方法,即将外部temp作为const对象。这将强制复制(因为移动操作需要非const源对象)。


-11
投票

我不确定它是否是另一个答案中提到的错误,但不知何故catch块在处理异常一次后改变/省略了temp的内容。下面的代码解决了这个问题。让temp成为const来解决这个问题。

#include <iostream>
    int main()
    {
        const 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::cerr << temp << "\n";
                  }
        }

        return 0;
    }
© www.soinside.com 2019 - 2024. All rights reserved.