在异常对象上调用 std::move 是不是正确?
Posted
技术标签:
【中文标题】在异常对象上调用 std::move 是不是正确?【英文标题】:Is it correct to call std::move on an exception object?在异常对象上调用 std::move 是否正确? 【发布时间】:2022-01-23 21:47:23 【问题描述】:我很想知道将异常对象移动到某个局部变量是否正确。这个动作有没有可能导致UB?我的担心是由于引用捕获假定访问位于其他地方的异常对象(因为它必须一直存在到堆栈展开结束)。请参见下面的示例。
int main()
std::pair<int,int> res; //may be heavy object, it's only example
int a[3][5];// assume filled
try
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 5; ++j)
if(a[i][j] % 2 ==0 )
throw std::pair<int,int>(i,j);
catch(std::pair<int,int>& pair)
res = std::move(pair);
【问题讨论】:
apair<int,int>
仍然会被复制。
异常不应是“重”对象。
移动一个异常对象很好:结果无论如何都需要是可破坏的,并且在大多数情况下你不会关心对象的状态。当然,如果您打算重新抛出异常,它会将其状态更改为某个不确定的状态(尽管在 std::pair<int, int>
移动对象的情况下不会影响原始对象 - 它将始终被复制)。
虽然我看不出有什么优势,但我认为不会有问题。一点旁注:不应在正常执行流程中抛出异常,而应仅针对罕见的异常事件。
自 C++11 起,捕获异常对象并将其寿命延长到 catch
块之外的标准方法是使用 std::current_exception()
,但我看不到任何访问它捕获的对象,您只能使用std::rethrow_exception()
重新抛出它。
【参考方案1】:
移出的对象仍处于有效状态。
即使你通过throw;
重新抛出异常,并再次捕获它,它也会起作用。
对于处于有效但未指定状态的对象(假设您使用这样的对象而不是一对整数),结果可能是意外的,但仍然没有 UB。
【讨论】:
【参考方案2】:由于您的代码没有通过为预期结果抛出异常来遵循良好实践,因此问题的答案实际上并不重要,因为无论如何您都应该重写代码...
这是一个关于如何编写此类代码的示例。
#include <utility>
std::pair<int, int> find(int(&a)[3][5])
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 5; ++j)
if (a[i][j] % 2 == 0)
return std::pair<int, int>(i, j);
return ;
int main()
int a[3][5];// assume filled
auto res = find(a);
虽然您的初始代码可以工作,但抛出和捕获异常可能会很慢,因此即使移动,您的代码也可能比不使用异常返回结果的版本慢。
一个好的做法是只对意外错误使用异常。
【讨论】:
以上是关于在异常对象上调用 std::move 是不是正确?的主要内容,如果未能解决你的问题,请参考以下文章
空范围的 std::copy() 或 std::move() 是不是需要有效目的地?
在 C++11 中对 boost::asio 套接字对象重复 std::move
如果 lambda 使用 std::move() 捕获不可复制的对象,为啥它是不可移动的?