块会影响封闭作用域中的变量

块会影响封闭作用域中的变量

本文介绍了为什么 Try-Catch 块会影响封闭作用域中的变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么捕获到第一个异常后,外层temp会变空?

Why does the outer temp become empty after catching first exception?

#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 编译我的代码.

I compile my code with g++ 7.3.0.

推荐答案

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

This appears to be a bug in GCC's implementation of copy elision. The C++ standard says the following:

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

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

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • 在 throw 表达式中,当操作数是非易失性自动对象的名称时(函数或 catch 子句除外)参数) 其作用域不超过最里面封闭的 try 块(如果有),复制/移动从操作数到异常对象的操作可以省略将自动对象直接构造成异常对象
  • in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object can be omitted by constructing the automatic object directly into the exception object

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

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

  • 如果 throw 表达式的操作数是非易失性自动对象的名称(函数或 catch 子句参数除外)其作用域不超出最里面封闭的 try 块的末尾(如果有)

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

This is a family of optimizations that allows the copy initialization of an exception object to be either avoided or done as efficiently as possible. Now, a common implementation of std::string move construction is to leave the source string empty. This appears to be exactly what happens to your code. The temp in the outer scope is moved from (and left empty).

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

But that is not the intended behavior. The scope of the temp you throw exceeds (by far) the try block it's thrown in. So GCC has no business applying copy elision to it.

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

A possible workaround is to place the declaration of temp inside the while loop. This initialized a new std::string object every iteration, so even if GCC moves from it, it won't be noticeable.

评论中提到了另一种解决方法,是使外部 temp 成为 const 对象.这将强制复制(因为移动操作需要非常量源对象).

Another workaround was mentioned in the comments and is to make the outer temp a const object. This will force a copy (since a move operation requires a non-const source object).

这篇关于为什么 Try-Catch 块会影响封闭作用域中的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-22 18:39