gpt4 book ai didi

c++ - 以现代算法形式写入 `for(x : y) if(p(x)) return x;`

转载 作者:太空狗 更新时间:2023-10-29 20:24:50 24 4
gpt4 key购买 nike

我总是尽可能使用类似 STL 的算法,因为它们简洁且表现力强。

我的一个库中有这段代码:

auto& findFlag(const std::string& mName)
{
for(auto& f : makeRangeCastRef<Flag>(getFlags()))
if(f.hasName(mName))
return f;

throw Exception::createFlagNotFound(mName, getNamesStr());
}

我想用现代 C++ 算法形式编写它,但我不知道如何处理早期的 return 和可能的 throw

for(auto& value : container) if(predicate(value)) return value; 
// ^~~~~~
// IMPORTANT: return from the caller function, not the algorithm itself

理想情况下,我想将真正的代码片段编写为:

auto& findFlag(const std::string& mName)
{
early_return_if(makeRangeCastRef<Flag>(getFlags()),
[&mName](const auto& f){ return f.hasName(mName); });

throw Exception::createFlagNotFound(mName, getNamesStr());
}

显然,像 early_return_if 这样的东西是不存在的——据我所知,没有办法从被调用者调用调用者函数的 returnreturn early_return_if(...) 可以工作,但如果不创建抛出异常的特定算法,我就无法抛出异常。

您有什么建议?应该保持代码原样,还是有任何类似算法的方法可以重写它?

编辑:

如评论中所述,std::find_if 是一个很好的候选者,但可以避免不必要的检查:

auto& findFlag(const std::string& mName)
{
auto container(makeRangeCastRef<Flag>(getFlags())); // Need to write this out...

// Need to keep track of the returned iterator...
auto it(findIf(container, [&mName](const auto& f){ return f.hasName(mName); }));

if(it != container.end()) return *it; // I don't like this either...

throw Exception::createFlagNotFound(mName, getNamesStr());
}

最佳答案

使用 boost::optional 的基于范围的算法. reference_type_t留作练习(提示:首先根据范围内 iterator_type_t 的 adl 查找编写 begin)。

template<class Range, class Function>
boost::optional< reference_type_t<Range> >
search_if( Range&& r, Function&& f ) {
for( auto&& x:std::forward<Range>(r) ) {
if (f(x))
return std::forward<decltype(x)>(x);
}
return {};
}

然后:

auto& findFlag(const std::string& mName) {
auto result = search_if(
makeRangeCastRef<Flag>(getFlags()),
[&](auto&& f){return f.hasName(mName); }
);
if (result) return *result;
throw Exception::createFlagNotFound(mName, getNamesStr());
}

您可以完全取消异常,并拥有 findFlag返回 optional本身(基本上是 search_if )。

不,您不能将流控制注入(inject)到调用您的函数中。

上面确实依赖于 optional支持可选引用。这些是有争议的:即将到来的std::optional我上次检查时不支持它们。

你也可以替换这样的optional s 与简单 T*

template<class Range, class Function>
value_type_t<Range>*
search_if( Range&& r, Function&& f ) {
for( auto&& x:std::forward<Range>(r) ) {
if (f(x))
return &x;
}
return nullptr;
}

但不利的是,如果您的范围很奇怪(例如 std::vector<bool> ),您最终会得到对上面临时值的引用。

value_type_t 的草图和 reference_type_t ,它采用范围/容器并输出该范围/容器值/引用类型:

namespace adl_aux {
using std::begin;
template<class R> using iterator_t = decltype( begin(std::declval<R>()) );
}
using adl_aux iterator_t;
template<class T>struct void{using type=void;}
template<class T>using void_t=typename void<T>::type;

template<class R,class=void>
struct value_type {};
template<class R>
struct value_type<R, void_t< iterator_t<R> > {
using type = std::iterator_traits< iterator_t<R> >::value_type;
};
template<class R>using value_type_t = typename value_type<R>::type;

template<class R,class=void>
struct reference_type {};
template<class R>
struct reference_type<R, void_t< iterator_t<R> > {
using type = std::iterator_traits< iterator_t<R> >::reference_type;
};
template<class R>using reference_type_t = typename reference_type<R>::type;

它可以变得更健壮——SFINAE 对迭代器的检查可以检查 begin 返回类型上的迭代器公理,并确保 end是相同的迭代器或兼容的哨兵。

关于c++ - 以现代算法形式写入 `for(x : y) if(p(x)) return x;`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26041226/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com