c++ - 我可以毫无例外地将 std::accumulate 与易错操作一起使用吗?
问题描述
我有一些操作想与 std::accumulate 一起使用,但是对于某些元素它可能会失败,在这种情况下应该中止累积。有了异常,我可以在失败的情况下抛出异常,但我需要构建没有异常。除了例外,这看起来像这样(大大简化了操作):
std::optional<int> sum_accumulate_with_exceptions(
std::vector<int> const& aVec) {
try {
return std::accumulate(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42)
throw std::logic_error{"too large"};
return oldSum + current;
});
} catch (std::logic_error const&) {
return std::nullopt;
}
}
实际上,即使有可能使用异常,这似乎也很浪费,因为我对抛出的特定异常不感兴趣,因此异常的开销太大了。
使用 std::accumulate,我可以使用这样的错误标志:
std::optional<int> sum_accumulate_without_exceptions(
std::vector<int> const& aVec) {
bool errored = false;
int res = std::accumulate(aVec.begin(), aVec.end(), 0,
[&errored](int oldSum, int current) {
if (errored) return 0;
if (current > 42) {
errored = true;
return 0;
}
return oldSum + current;
});
return errored ? std::optional<int>{} : res;
}
但是,这显然很糟糕,因为它总是迭代整个容器,这可能很大。
我想出了我自己的 std::accumulate 变体:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
for (auto it = aBegin; it != aEnd; ++it) {
res = aOp(*res, *it);
if (!res) break;
}
return res;
}
这可以很好地用于如下示例:
std::optional<int> sum_accumulate_shortcircuit(std::vector<int> const& aVec) {
return accumulate_shortcircuit(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42) {
return std::optional<int>{};
}
return std::optional<int>{oldSum + current};
});
}
但是,我更喜欢使用 std::accumulate (或任何其他标准库算法 [edit:] 或它们的组合)本身,而不是使用替换。有什么办法可以做到这一点?
虽然我在示例中使用 C++17 的 std::optional ,但理想情况下这只会使用 C++14 标准库算法,但我也对来自较新/未来标准版本的解决方案感兴趣。
[编辑:]根据@NathanOliver的回答,可以像这样实现accumulate_shortcircuit,而无需范围TS:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
std::all_of(aBegin, aEnd, [&](const T& element) {
return static_cast<bool>(res = aOp(*res, element));
});
return res;
}
解决方案
您需要一种内置短路的算法。首先想到的是std::any_of
. 您可以使用 lambda 进行求和,然后在到达要返回的点后返回 true。那会给你一个像
int sum_accumulate_shortcircuit(std::vector<int> const& aVec)
{
int sum = 0;
std::any_of(aVec.begin(), aVec.end(),
[&](auto elm) { if (elm > 42) return true; sum += elm; return false; });
return sum;
}
推荐阅读
- android - listFiles() 不显示 Android 11 中扩展名很少的文件
- android - 为什么华为Push Kit发布应用后无法使用?
- javascript - youtube player api: Uncaught TypeError: a.next is not a function when loading script from www.youtube.com/iframe_api
- php - 奇怪的 PHP DIE() 行为
- python - 从具有值列表的字典创建 Pyqt QtableWidget,然后返回字典
- swift - 为什么会出现此错误消息,无法将类型“User.Type”的值转换为关闭结果类型“EventLoopFuture<_>”?
- reactjs - useSWR return { null, null } 表示有效请求
- java - 如何让我的 DTO 仅具有我已链接到我的普通类的完整对象的 ID,并在 Java 中使用 OneToMany 注释
- powershell - 在网络位置托管 Powershell 模块?
- laravel - Ubuntu 为 laravel 项目安装 rdkafka