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