之前一直很在意,std::bind会不会引入一些变量生命周期导致的问题呢?于是进行了以下测试,主要想了解以下几点:
- 被std::bind绑定的变量生命周期是怎么样的?
- 如何降低std::bind绑定过程的消耗?
- 使用std::function 来进行保存的会是怎样?
以下Test主要考察1,2两点,而Test2主要考察第三点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#include <stdio.h> #include <functional> using std::bind; class Inf { public: Inf() { printf("Inf Constructor Called. %p\n",this); } Inf(const Inf& ref) { printf("Inf Copy Constructor Called. src:%p,dst:%p\n", &ref, this); } Inf(Inf&& ref) { printf("Inf Move Constructor Called. src:%p,dst:%p\n", &ref, this); } Inf& operator=(const Inf& ref) { printf("Inf Copy Constructor Called. src:%p,dst:%p\n", &ref, this); return *this; } ~Inf() { printf("Inf Destructor Called. %p\n",this); } void Daisuke() const { printf("Daisuke!\n"); } }; int main() { auto TestFunc = [](Inf w) { w.Daisuke(); }; auto TestFunc2 = [](const Inf& w) { printf("Dai Dai Dai"); w.Daisuke(); }; auto TestFunc3 = [](Inf&& w) { printf("Dai Dai Dai Dai"); w.Daisuke(); };//无法Bind Inf obj; printf("==Test Start==\n"); { { auto bindFunc = bind(TestFunc, obj);//两次复制构造 bindFunc(); auto TestFunc2 = [bindFunc]() { bindFunc(); //无法调用 }; TestFunc2(); } printf("==bindFunc1 End.==\n"); { auto bindFunc = bind(TestFunc2, obj);//一次复制构造 bindFunc(); auto TestFunc2 = [bindFunc]() { bindFunc(); }; TestFunc2(); } printf("==bindFunc2 End.==\n"); { auto bindFunc = bind(TestFunc2, std::move(obj));;//不过也可以通过std::move来回避复制构造 bindFunc(); } } printf("==Test 2==\n"); { auto bindFunc = bind(TestFunc2, obj); printf("==bindFunc Created==\n"); auto bindFuncObj = std::function<void()>(bindFunc); bindFuncObj(); auto bindFuncObj2 = std::function<void()>(bindFuncObj); bindFuncObj(); auto bindFuncObj3 = std::function<void()>(std::move(bindFunc)); bindFuncObj3(); } scanf_s("%c"); } |
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
Inf Constructor Called. 00F0FF04 ==Test Start== Inf Copy Constructor Called. src:00F0FF04,dst:00F0FF06 Inf Copy Constructor Called. src:00F0FF06,dst:00F0FF05 Daisuke! Inf Destructor Called. 00F0FF05 Inf Copy Constructor Called. src:00F0FF06,dst:00F0FF07 Inf Copy Constructor Called. src:00F0FF07,dst:00F0FF05 Daisuke! Inf Destructor Called. 00F0FF05 Inf Destructor Called. 00F0FF07 Inf Destructor Called. 00F0FF06 ==bindFunc1 End.== Inf Copy Constructor Called. src:00F0FF04,dst:00F0FF07 Dai Dai DaiDaisuke! Inf Copy Constructor Called. src:00F0FF07,dst:00F0FF05 Dai Dai DaiDaisuke! Inf Destructor Called. 00F0FF05 Inf Destructor Called. 00F0FF07 ==bindFunc2 End.== Inf Move Constructor Called. src:00F0FF04,dst:00F0FF05 Dai Dai DaiDaisuke! Inf Destructor Called. 00F0FF05 ==Test 2== Inf Copy Constructor Called. src:00F0FF04,dst:00F0FF06 ==bindFunc Created== Inf Copy Constructor Called. src:00F0FF06,dst:00F0FF07 Inf Move Constructor Called. src:00F0FF07,dst:01070D2C Inf Destructor Called. 00F0FF07 Dai Dai DaiDaisuke! Inf Copy Constructor Called. src:01070D2C,dst:01070C2C Dai Dai DaiDaisuke! Inf Move Constructor Called. src:00F0FF06,dst:00F0FF07 Inf Move Constructor Called. src:00F0FF07,dst:01070B9C Inf Destructor Called. 00F0FF07 Dai Dai DaiDaisuke! Inf Destructor Called. 01070B9C Inf Destructor Called. 01070C2C Inf Destructor Called. 01070D2C Inf Destructor Called. 00F0FF06 |
观察运行结果可以得出以下的结论:
Test1:
- std::bind是通过复制一个绑定的对象,使得对象生命周期与std::bind生成的函数对象相同。
- 当要调用的函数没有使用传引用方式时,绑定过程会调用2次复制构造函数(追记:函数调用时的额外一次,跟std::bind无关),反之1次。
- 复制构造函数的调用可以通过std::move转变成移动构造函数
- std::bind无法绑定使用&&(完美转发?)的参数。
Test2:
- function<>也是在自己里面保存了一个std::bind产生的函数对象。
- 里面并没有采用shared_ptr相关的。
- 记得用std::move~(虽然似乎对于小对象也没什么区别吧)
PS1:说起来在std::bind的实现中大部分地方都是用完美转发的,只有传给Tuple<>进行对象的保存时会调用构造函数的…(这样说来调用两次构造函数有一次不是std::bind的锅啊…..失算)
PS2:似乎std::bind与lambda表达式的变量捕获共同使用有时会出问题,原因不明,建议拿std::function打个包…