为什么外部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-expression中,当操作数是非易失性自动对象(函数或catch子句参数除外)的名称时,其范围不会超出最内层封闭try-block的末尾(如果有的话) ),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象的复制/移动操作
在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:
- 如果throw-expression的操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会超出最内层封闭try-block的末尾(如果有的话) ,
这是一系列优化,允许避免或尽可能有效地完成异常对象的复制初始化。现在,std::string
移动构造的常见实现是将源字符串留空。这似乎正是您的代码所发生的事情。外部范围中的temp
从(并留空)移动。
但这不是预期的行为。你抛出的temp
的范围超过(到目前为止)它所引入的try块。所以GCC没有业务应用复制省略。
一个可能的解决方法是将temp
的声明放在while
循环中。这会在每次迭代时初始化一个新的std::string
对象,所以即使GCC
从它移动,它也不会引人注意。
注释中提到了另一种解决方法,即将外部temp
作为const对象。这将强制复制(因为移动操作需要非const源对象)。
我不确定它是否是另一个答案中提到的错误,但不知何故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;
}