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