让我描述一个场景,首先我们有一个函数,它返回一些我们无法确定其有效性的数据,即
auto random_predicate() -> bool
{
int v = uniform_dist(e1); // uniform distribution 1 to 100
return (v % 5);
}
Run Code Online (Sandbox Code Playgroud)
其中uniform_dist()是适当种子的均匀分布,我们有一个enum class将用于错误处理的,即
enum class Error
{
ValueError
};
Run Code Online (Sandbox Code Playgroud)
然后我们执行某种基于视图的处理,该处理random_predicate()在操作中使用,如下所示:
std::vector<int> vs{1,2,3,4,5};
auto result = vs
| views::filter([](int i){ return i % 2; })
| views::transform([](int i) -> std::expected<int, Error> {
auto v = random_predicate();
if (v) return std::unexpected<Error>(Error::ValueError);
else return i * i;
});
Run Code Online (Sandbox Code Playgroud)
因此,在该操作结束时,我们可以断言
static_assert(
std::is_same_v<
std::decay_t<std::ranges::range_value_t<result>>,
std::expected<int, Error>
>
)
Run Code Online (Sandbox Code Playgroud)
事实上将会是真的。
问题是,然后呢?我们知道有一个需要解析为的值视图std::expected:向上传播调用堆栈的错误类型,或成功类型的视图(即上面示例中的视图int(所有元素不是 5 的倍数) !))
我的解决方案是简单地检查每个元素是否有错误,然后如果没有错误,则将结果转换为所需的视图,所以类似
template<typename T>
static auto has_error(const std::expected<T, Error>& e){ return !e.has_value(); };
auto f(const std::vector<int>& vs)
{
auto c = vs
| views::filter([](int i){ return i % 2; })
| views::transform([](int i) -> std::expected<int, Error> {
auto v = random_predicate();
if (v) return std::unexpected<Error>(Error::ValueError);
else return i * i;
});
if (auto v = ranges::find_if(c, has_error<int>); v != c.end())
{
return (*v).error();
}
else
{
return c | views::transform([](auto&& e){ return e.value(); });
}
}
Run Code Online (Sandbox Code Playgroud)
但随后我们遇到了一个问题,即该函数无法将返回类型推断为std::expected<T, Error>具有T类型元素的容器的类型(在上面的示例中)int。好吧,我什至不知道在这里要写什么T,所以我的问题是应该如何实现?
神螺栓: https: //godbolt.org/z/Wfjr8o3qM
或者,我有兴趣听听其他人如何以更好的方式共同解决这个问题?
谢谢
编辑:我想,您真的不想返回某些元素的视图,因为它可能会导致悬空视图?在这种情况下,最好只ranges::to<T>()在从函数返回时使用吗?
仍然能够返回范围的另一个选项:
auto f(const std::vector<int> &vs) {
auto c = vs
| views::filter([](int i) { return i % 2; })
| views::transform([](int i) -> std::expected<int, Error> {
auto v = random_predicate();
if (v) return std::unexpected<Error>(Error::ValueError);
else return i * i;
});
auto values = c | views::transform([](auto &&e) { return e.value(); });
using success_t = decltype(values);
using ret_t = std::expected<success_t, Error>;
if (auto v = ranges::find_if(c, has_error<int>); v != c.end()) {
return ret_t(std::unexpected<Error>((*v).error()));
} else {
return ret_t(values);
}
}
Run Code Online (Sandbox Code Playgroud)
它利用了这样一个事实:值作为视图是延迟计算的。因此,如果不返回,则永远不会计算值,我们可以使用它来确定返回类型。下一步是确保所有返回都包装到ret_t类型中,以便 auto 可以正确猜测。
注意:在这个答案和您原来的问题中,这只适用于原始范围,可以多次迭代(抱歉不记得这个概念的名称)
| 归档时间: |
|
| 查看次数: |
1246 次 |
| 最近记录: |