我有一些我想与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标准库算法,但我也对更新/将来的标准版本的解决方案感兴趣。
[edit:]基于@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;
}
答案 0 :(得分:4)
您需要一种内置短路的算法。想到的第一个是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;
}
答案 1 :(得分:2)
供将来参考,在C ++ 20中(包括范围TS),这种类型的算法/运算组合将更加容易。这是当前使用accumulate
和view::take_while
的TS的示例:
auto sum = ranges::accumulate(my_vec | view::take_while([] (auto i) -> i <= 42), 0);