C++ 如果(p(x))返回x;`以现代算法形式

C++ 如果(p(x))返回x;`以现代算法形式,c++,algorithm,lambda,return,c++14,C++,Algorithm,Lambda,Return,C++14,在可能的情况下,我总是尝试使用类似STL的算法,因为它们简洁且极具表现力 我的一个库中有以下代码: auto& findFlag(const std::string& mName) { for(auto& f : makeRangeCastRef<Flag>(getFlags())) if(f.hasName(mName)) return f; throw Exception::createFla

在可能的情况下,我总是尝试使用类似STL的算法,因为它们简洁且极具表现力

我的一个库中有以下代码:

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

    throw Exception::createFlagNotFound(mName, getNamesStr());
}
理想情况下,我希望将真正的代码片段编写为:

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());
}

一种基于范围的算法,使用
boost::optional
<代码>引用类型\u t作为练习离开(提示:首先基于范围上的
开始
的adl查找写入
迭代器类型\u t

但缺点是,如果您的范围很奇怪(如
std::vector
),您最终会引用上面的临时变量

value\u type\t
reference\u 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;
namespace adl_aux{
使用std::begin;
使用迭代器的模板(begin(std::declval());
}
使用adl_辅助迭代器;
templatestruct void{using type=void;}
模板使用void\u t=typename void::type;
模板
结构值_类型{};
模板
结构值类型{
使用type=std::iterator\u traits::value\u type;
};
templateusing value\u type\u t=typename value\u type::type;
模板
结构引用类型{};
模板
结构引用类型{
使用type=std::iterator\u traits::reference\u type;
};
templateusing reference\u type\u t=typename reference\u type::type;

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

我认为对于这种特殊情况,使用循环是最具表现力且通常是最好的解决方案。

我不确定究竟是什么使RangeCastref()成为您的其他一些代码正在运行,但我个人认为find_if版本比原始版本更可读,如果您编写的代码更像这样:

auto& findFlag(const std::string& mName)
{
    auto findIt = find_if(cbegin(getFlags()), cend(getFlags()), [&](const auto& f){ return f.hasName(mName); });
    if (findIt == container.end()) throw Exception::createFlagNotFound(mName, getNamesStr());
    return *findIt;
}
对我来说,检查异常条件(未找到标记)并抛出异常似乎更自然,否则会进入返回找到的项的正常退出路径,而不是在“正常”条件下从循环内部返回的基于循环的版本中,使用Alexandrescu,您可以编写一个算法,返回一个可转换为您正在查找的元素的对象,或者在未找到该对象时抛出异常。类似(未编译此文件):

模板
Expexted find_if_ref(It first,It last,Pred Pred,Else el)
{
自动it=查找(第一个、最后一个、pred);
if(it==last){
试一试{
el();
}
捕获(…){
返回std::current_exception();
}
}
归还它;
}

听起来像是一个很好的例子,用于out of
auto it=std::find_if(begin(container)、end(container)、predicate);还它!=结束(容器)*它:抛出/*…*/@T.C.,@Cyber:我考虑过使用
std::find_if
,但是检查
it!=end(container)
是不必要的,我想避免that@VittorioRomeo您有
O(N)
比较来查找元素,并且您关心的是(仅)在所有元素都失败的情况下,您将获得一个额外的?坚持循环,以便大声呼喊。在不需要算法的地方强制使用算法,这是对代码的伤害,因为它们被认为是“现代的”(SGI STL从1994年开始出现,IIANM)。
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());
}
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;
}
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;
auto& findFlag(const std::string& mName)
{
    auto findIt = find_if(cbegin(getFlags()), cend(getFlags()), [&](const auto& f){ return f.hasName(mName); });
    if (findIt == container.end()) throw Exception::createFlagNotFound(mName, getNamesStr());
    return *findIt;
}
template <class It, class Pred, class Else>
Expexted<T&> find_if_ref(It first, It last, Pred pred, Else el)
{
    auto it = find_if(first, last, pred);
    if (it == last) {
        try {
            el();
        }
        catch (...) {
            return std::current_exception();
        }
    }
    return *it;
}