• C++ Defer 操作


    C++ Defer

    C++ 中并没有官方的defer操作,所以需要自己实现一个。

    跟一个guard函数类似,在一个栈对象的析构函数中调用defer函数,std::function 是一个不错的选择,可以bind一个已经存在的函数,也可以使用lambda表达式,所以第一个版本defer长这样:

    class Defer {
    public:
        Defer(std::function<void()> defer): _defer(std::move(defer)) {}
        ~Defer() { _defer(); }
    private:
        std::function<void()> _defer;
    };
    
    // 使用:
    Defer defer1([]() { std::cout << "defer op" << std::endl;});
    
    std::function<void()> func = []() { std::cout << "defer op" << std::endl;};
    Defer defer2(func);
    

    std::function很强大,但是代价也很高,在创建函数对象的时候总是会有 new操作的。虽然通常情况下影响不是很高,但是总觉得这是没必要的。

    下面是 GCC 中functional实现

    private:
      static void
      _M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
      { ::new (__functor._M_access()) _Functor(std::move(__f)); }
    
      static void
      _M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
      { __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); }
    

    当然这个不是lambda表达式的问题,因为lambda表达式的创建是基本没有开销的,在C++中lambda表达式通常的实现是用一个仿函数来实现的。例如:

    void func_call() {
       int var1 = xxx;
       std::string var2 = xxx;
       auto lambda = [var1, &var2]() mutable {
           // do something
       };
       lambda();
    }
    
    // 通常编译器会处理为
    
    void func_call() {
        struct func_call#lambda1 {
            func_call#lambda1(int v1,std::string& v2):_v1(v1),_v2(v2) {}
            operator ()() {
                // dosome thing
            }
        private:
            var1 _v1;
            var2 &_v2;
        }
        func_call#lambda1();
    }
    

    所以lambda表达式的构造和复制的开销的非常低的。

    我们基于这个思路来做第二版的defer操作:

    template <class T>
    class LambdaDefer {
    public:
        LambdaDefer(T& closure) : _closure(closure) {}
        ~LambdaDefer() { _closure(); }
    
    private:
        T& _closure;
    };
    
    // 使用:
    auto deferop = [&]() { std::cout << "defer" << std::endl;};
    LambdaDefer<decltype(deferop)> defer(deferop);
    

    性能测试在这里:https://quick-bench.com/q/4IKpxAA5VEbXVziV1G2Po-ZupSE

    性能表现符合预期,但是使用起来太麻烦了。

    因为想要构建一个LambdaDefer,必须要显示的指定模板类型,C++11大致是止步于此了。

    不过 C++17 就有更好的实现方式了,因为有类型推导这个神奇的操作:

    // c++ 11
    auto pair = std::make_pair<int, double>(1, 1.2);
    // c++ 17
    auto pair = std::make_pair(1, 1.2);
    

    按照这个思路我们可以这样搞:

    template <class T>
    class Defer {
    public:
    	Defer(T& closure) : _closure(closure) {}
    	Defer(T&& closure) : _closure(std::move(closure)) {}
    	~Defer() { _closure(); }
    private:
    	T _closure;
    };
    
    // only C++17 support
    auto deferop = []() { std::cout << "defer" << std::endl; };
    Defer defer {deferop};
    Defer defer2 {[]() { std::cout << "defer" << std::endl; }};
    
  • 相关阅读:
    第一章 快速入门
    增量式PID控制算法
    第二章 变量和基本类型
    位置式PID和增量式PID区别?
    I2C
    Linux系统进程调用列表
    Linux下I/O模型
    Linux下多路复用接口
    新浪博客网页编辑器PHP版带有上传图片功能
    晕死!博客园把我搞晕了!
  • 原文地址:https://www.cnblogs.com/stdpain/p/14660678.html
Copyright © 2020-2023  润新知