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 编译我的代码。
解决方案
这似乎是 GCC 复制省略实现中的一个错误。C++ 标准规定如下:
[class.copy.elision](强调我的)
这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):
- 在 throw 表达式中,当操作数是非易失性自动对象(函数或 catch 子句参数除外)的名称时,其范围不超出最内层封闭 try 块的末尾(如果有),可以通过将自动对象直接构造到异常对象中来省略从操作数到异常对象的复制/移动操作
在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:
- 如果 throw 表达式的操作数是非易失性自动对象的名称(函数或 catch 子句参数除外) ,其范围不超出最内层封闭 try 块的末尾(如果有) ,
这是一系列优化,允许避免或尽可能高效地完成异常对象的复制初始化。现在,移动构造的一个常见实现std::string
是将源字符串留空。这似乎正是您的代码发生的情况。外部范围内的temp
被移出(并留空)。
但这不是预期的行为。temp
你抛出的范围超过了(到目前为止)它被抛出的 try 块。所以 GCC 没有业务对其应用复制省略。
一种可能的解决方法是将声明temp
放在while
循环内。这在每次迭代时都会初始化一个新std::string
对象,因此即使GCC
从它移开,它也不会引起注意。
评论中提到了另一种解决方法,即使外部temp
成为 const 对象。这将强制进行复制(因为移动操作需要非常量源对象)。
推荐阅读
- security - Docker Image 编译会有副作用吗?
- hibernate - EJB 事务回滚导致不需要的数据库更改/对象持久性
- c++ - 模板接受 const 但不接受字面量
- sql - PL/SQL 中的 SQL 注入 - 神话还是事实?
- angular - 配置路由保护
- linux - arecord 文件类型标头是否在开始时流式传输?
- javascript - 将值输入输入(用户名、密码、电子邮件等)并使用 javascript 提交
- json - 我需要从我的 component.ts 文件中保存的 JSON 对象中获取数据
- r - 在 R Shiny 的 UI 中的 IF 语句中使用反应值
- android - 是否可以将 DataSnapShot 更改为模型?