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