jjzjj

c++ - 在基于 lambda 的 foreach 循环中模拟 `continue;` , `break;`

coder 2023-11-13 原文

我是“基于 lambda 的 foreach 循环”的粉丝:

class SomeDataStructure
{
    private:
        std::vector<SomeData> data;

    public:
        template<typename TF> void forData(TF mFn)
        {
            for(int i{0}; i < data.size(); ++i)
                mFn(i, data[i]);
        }
};

SomeDataStructure sds;

int main()
{
   sds.forData([](auto idx, auto& data)
   {
       // ...
   });
}

我认为它是更复杂数据结构的一个很好的抽象,因为它允许用户直观地循环使用附加参数的内容。编译器优化应保证性能与传统的 for(...) 循环相同。

不幸的是,像这样使用 lambda 显然会阻止使用有时有用的 continue;break; 语句。

sds.forData([](auto idx, auto& data)
{
    // Not valid!
    if(data.isInvalid()) continue;
});

有没有什么方法可以模拟 continue;break; 语句而不会有任何性能损失并且不会降低语法的便利性?

最佳答案

用产生迭代器的成员函数beginend替换成员函数forData,然后替换

sds.forData([](auto idx, auto& data)
{
    // Not valid!
    if(data.isInvalid()) continue;
});

for( auto& data : sds )
{
    if(data.isInvalid()) continue;
}

但是,如果出于某些未公开的原因您更愿意使用 forData 成员函数,那么您可以通过以下方式实现伪continuebreak一些滥用异常。例如,Python 的 for 循环是基于异常的。 forData 驱动程序代码将忽略继续异常,并通过停止迭代来处理中断异常。

template<typename TF> void forData(TF mFn)
{
    for(int i{0}; i < data.size(); ++i)
    {
        try
        {
            mFn(i, data[i]);
        }
        catch( const Continue& )
        {}
        catch( const Break& )
        {
            return;
        }
    }
}

另一种方法是要求 lambda 返回一个表示“break”或“continue”的值。

最自然的做法是为此使用枚举类型。

在我看来,返回值方法的主要问题是它劫持了 lambda 结果值,例如然后它不能(非常容易地)用于产生循环累积的结果,或类似的东西。


不过,我不会这样做。我宁愿使用基于范围的 for 循环,正如本答案开头所推荐的那样。但如果您这样做,并且担心效率,那么请记住要做的第一件事就是衡量。


附录:添加一个类似 Python 的枚举函数。

你可以实现一个类似 Python 的枚举函数,它产生一个集合的逻辑 View ,这样集合看起来是(值,索引)对的集合,非常适合在基于范围的 for 循环:

cout << "Vector:" << endl;
vector<int> v = {100, 101, 102};
for( const auto e : enumerated( v ) )
{
    cout << "  " << e.item << " at " << e.index << endl;
}

以下代码(最低限度,只是为了这个答案而拼凑而成)显示了一种执行此操作的方法:

#include <functional>       // std::reference_wrapper
#include <iterator>         // std::begin, std::end
#include <utility>          // std::declval
#include <stddef.h>         // ptrdiff_t
#include <type_traits>      // std::remove_reference

namespace cppx {
    using Size = ptrdiff_t;
    using Index = Size;
    template< class Type > using Reference = std::reference_wrapper<Type>;

    using std::begin;
    using std::declval;
    using std::end;
    using std::ref;
    using std::remove_reference;

    template< class Derived >
    struct Rel_ops_from_compare
    {
        friend
        auto operator!=( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) != 0; }

        friend
        auto operator<( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) < 0; }

        friend
        auto operator<=( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) <= 0; }

        friend
        auto operator==( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) == 0; }

        friend
        auto operator>=( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) >= 0; }

        friend
        auto operator>( const Derived& a, const Derived& b )
            -> bool
        { return compare( a, b ) > 0; }
    };

    template< class Type >
    struct Index_and_item
    {
        Index               index;
        Reference<Type>     item;
    };

    template< class Iterator >
    class Enumerator
        : public Rel_ops_from_compare< Enumerator< Iterator > >
    {
    private:
        Iterator        it_;
        Index           index_;
    public:
        using Referent = typename remove_reference<
            decltype( *declval<Iterator>() )
            >::type;

        friend
        auto compare( const Enumerator& a, const Enumerator& b )
            -> Index
        { return a.index_ - b.index_; }

        auto operator->() const
            -> Index_and_item< Referent >
        { return Index_and_item< Referent >{ index_, ref( *it_ )}; }

        auto operator*() const
            -> Index_and_item< Referent >
        { return Index_and_item< Referent >{ index_, ref( *it_ )}; }

        Enumerator( const Iterator& it, const Index index )
            : it_( it ), index_( index )
        {}

        auto operator++()
            -> Enumerator&
        { ++it_; ++index_; return *this; }

        auto operator++( int )
            -> Enumerator
        {
            const Enumerator result = *this;
            ++*this;
            return result;
        }

        auto operator--()
            -> Enumerator&
        { --it_; --index_; return *this; }

        auto operator--( int )
            -> Enumerator
        {
            const Enumerator result = *this;
            --*this;
            return result;
        }
    };

    template< class Collection >
    struct Itertype_for_ { using T = typename Collection::iterator; };

    template< class Collection >
    struct Itertype_for_<const Collection> { using T = typename Collection::const_iterator; };

    template< class Type, Size n >
    struct Itertype_for_< Type[n] > { using T = Type*; };

    template< class Type, Size n >
    struct Itertype_for_< const Type[n] > { using T = const Type*; };

    template< class Collection >
    using Itertype_for = typename Itertype_for_< typename remove_reference< Collection >::type >::T;


    template< class Collection >
    class Enumerated
    {
    private:
        Collection&     c_;
    public:
        using Iter = Itertype_for< Collection >;
        using Eter = Enumerator<Iter>;

        auto begin()    -> Eter { return Eter( std::begin( c_ ), 0 ); }
        auto end()      -> Eter { return Eter( std::end( c_ ), std::end( c_ ) - std::begin( c_ ) ); }

        //auto cbegin() const -> decltype( c_.cbegin() )  { return c_.cbegin(); }
        //auto cend() const   -> decltype( c_.cend() )    { return c_.cend(); }

        Enumerated( Collection& c )
            : c_( c )
        {}
    };

    template< class Collection >
    auto enumerated( Collection& c )
        -> Enumerated< Collection >
    { return Enumerated<Collection>( c ); }

}  // namespace cppx

#include <iostream>
#include <vector>
using namespace std;

auto main() -> int
{
    using cppx::enumerated;

    cout << "Vector:" << endl;
    vector<int> v = {100, 101, 102};
    for( const auto e : enumerated( v ) )
    {
        cout << "  " << e.item << " at " << e.index << endl;
    }

    cout << "Array:" << endl;
    int a[] = {100, 101, 102};
    for( const auto e : enumerated( a ) )
    {
        cout << "  " << e.item << " at " << e.index << endl;
    }
}

关于c++ - 在基于 lambda 的 foreach 循环中模拟 `continue;` , `break;`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28897974/

有关c++ - 在基于 lambda 的 foreach 循环中模拟 `continue;` , `break;`的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  3. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  7. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  8. ruby-on-rails - 在这种情况下我如何模拟一个对象?没有明显的方法可以用模拟替换对象 - 2

    假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl

  9. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  10. ruby - 在 RSpec 中 stub /模拟全局常量 - 2

    我有一个gem,它有一个根据Rails.env的不同行为的方法:defself.envifdefined?(Rails)Rails.envelsif...现在我想编写一个规范来测试这个代码路径。目前我是这样做的:Kernel.const_set(:Rails,nil)Rails.should_receive(:env).and_return('production')...没关系,只是感觉很丑。另一种方法是在spec_helper中声明:moduleRails;end而且效果也很好。但也许有更好的方法?理想情况下,这应该有效:rails=double('Rails')rails.sho

随机推荐