for_each 在出现异常时的用法? std::exception_list

Usage of for_each in the presence of exceptions? std::exception_list

cppreference 文档 https://en.cppreference.com/w/cpp/algorithm/for_each 说:

  • If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the three standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.

我认为这意味着我不能开箱即用地从 for_each 传递的函数中抛出并期望捕获异常或与之相关的一些信息。

我期望使用异常的原因是我可以部分撤消(还原)在 for_each 调用中所做的更改。 (也许有更好的算法)。

然而,我偶然发现了 for_each 的一个历史版本,它被记录为具有不同的、更有趣的行为:

http://man.hubwiz.com/docset/C.docset/Contents/Resources/Documents/output/en/cpp/algorithm/for_each.html

  • if policy is std::parallel_vector_execution_policy, std::terminate is called
  • if policy is std::sequential_execution_policy or std::parallel_execution_policy, the algorithm exits with an std::exception_list containing all uncaught exceptions. If there was only one uncaught exception, the algorithm may rethrow it without wrapping in std::exception_list. It is unspecified how much work the algorithm will perform before returning after the first exception was encountered.

这似乎意味着可以实际使用异常而不是 terminateing。

所以,为什么std::exception_list被淘汰了?,是不是争议太大,太复杂,太(内存)费?

即使我同意逻辑,我真的没有任何其他选择,因为并行for_each returns void(而不是后面的UnaryFunction,这也是令人惊讶的). 所以, 在我看来,此 std::exception_list 协议是撤消未能完成的 for_each 指令的必要组成部分。

期望一些新的自定义策略是否合理,例如par_with_failed_list 会出现在允许 undoing 的地方。


更多上下文:这种撤消失败循环的模式用于构建容器。我想实现一个自定义 (parallel/sequencial) uninitialized_value_construct_n 当(任何未排序的)构造失败时 "undo's" (销毁)初始化对象。


EDIT1:尽管如此,也许可以将 lambda 中捕获的变量传递给函数参数。 这个变量可以是一个共享的并发数据,可以在异常发生时存储它们(作为 exception_list)。 我想知道这是否已经完成了。


EDIT2:我在 HPX 中找到了 exception_list 的实现,
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/exception_list.hpp
https://github.com/STEllAR-GROUP/hpx/blob/master/src/exception_list.cpp

std::exception_list 为并行算法的规范和实现增加了很多复杂性,却没有多少相应的收益。

作为用户,您可以在仿函数中处理这种情况:

struct exception_info{
    ElementType* element;
    std::exception_ptr exception;
};
std::vector<exception_info> exceptions;
std::mutex exceptions_mutex;

std::vector<ElementType> range=...;

std::for_each(std::execution::par,range.begin(),range.end(),[&](ElementType& element){
    try{ do_stuff(element); }
    catch(...){
        std::lock_guard guard(exceptions_mutex);
        exceptions.push_back({&element,std::current_exception()});
    }});

exceptions 列表现在将包含指向抛出异常的元素和抛出的异常的指针列表。