为什么外部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编译代码。
答案 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对象。这将强制执行复制操作(因为移动操作需要非常量源对象)。