模板参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <string> template <typename F> void print(F const &f){ std::cout<<f()<<std::endl; } int main() { std::cout << "Hello, World!" << std::endl; int num = 101; auto a = [&] () ->std::string { return std::to_string(num); }; print(a); num++; print(a); return 0; }
|
std::function
编译器需要支持c++11标准,有的编译器可能需要添加参数-std=c++11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <functional> #include <string> void print(std::function<std::string ()> const &f){ std::cout<<f()<<std::endl; } int main() { std::cout << "Hello, World!" << std::endl; int num = 101; auto a = [&] () ->std::string { return std::to_string(num); }; print(a); num++; print(a); return 0; }
|
std::function
是个模板类;模板参数:返回值类型+函数参数类型列表;例如std::function<int (int, int)> f
,f
可以指向任意一个返回指为int,参数为两个int
类型的函数。
观察上面的程序,lambda表达式无参数,返回值类型为string
;故在print
函数中声明一个类型为function<std::string ()>
类型的变量f
,就可以用来接收a
。注意:f
前面必须加上const
,否则编译过不了。至于加不加&,需要视程序而定,一般加上。
把lambda表达式作为子线程的参数
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
| #include <iostream> #include <thread> #include <chrono> #include <vector> #include <atomic> int main() { std::atomic_uint num(0); auto a = [&]() -> void { std::this_thread::sleep_for(std::chrono::milliseconds(num*20)); num += 100; }; std::thread t(a); t.join(); int value = num; printf("%d\n",value); auto b = [&](int i) -> void { std::this_thread::sleep_for(std::chrono::milliseconds(i)); num += i; }; std::thread tt(b,1000); tt.join(); value = num; printf("%d\n",value); return 0; }
|
陷阱:
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
| #include <iostream> #include <string> #include <thread> #include <chrono> #include <vector> int main() { std::vector<std::thread> _threads; for(int i=0;i<20;i++){ auto a = [=]() -> std::string { std::this_thread::sleep_for(std::chrono::milliseconds(i*20)); return std::to_string(i); }; auto wrapper = [&]() { std::string str = a(); std::cout<<str<<"\t"; }; _threads.emplace_back(std::thread(wrapper)); } for(auto& t : _threads) { if(t.joinable()){ t.join(); } } std::cout<<std::endl; return 0; }
|
观察上面的代码,理论上应该输出0~19。运行上面的程序,实际输出为
1 2 3
| 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19
Process finished with exit code 0
|
并不是预想的输出。直接调用wrapper(),可以得到正确的输出,但是把wrapper作为线程参数参数传进去时,结果却不正确。经过一番尝试,把定义wrapper的代码更改为如下代码
1 2 3 4 5 6
| auto wrapper = [=]() { std::string str = a(); std::cout<<str<<"\t"; };
|
再运行上面的程序,得到的结果为
1 2 3
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Process finished with exit code 0
|
初步的原因分析为,定义wrapper时,以引用的方式捕获a。然而在for循环中,虽然在更新a的定义,但是a的存储地址是固定的,故以引用的方式捕获a时,所有wapper里面的a都是最后一次更新的a。在子线程中调用wrapper时,自然都调用到在for循环中最后一次更新的a。定义wrapper时,以值的方式捕获a时,wrapper中保存的是a的副本,即使for循环中在更新a,也不影响之前定义的wapper。所以,在wrapper中,一定要以值的方式捕获用到的变量。
参考来源: https://blog.csdn.net/DumpDoctorWang/article/details/81903140