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