【初阶与进阶C++详解】第二十二篇:C++11新特性(列表初始化+变量类型推到+右值引用+新增默认成员函数+可变模板参数+lambda表达式+包装器function_bind)
🏆個人主頁:企鵝不叫的博客
? 🌈專欄
- C語言初階和進階
- C項目
- Leetcode刷題
- 初階數據結構與算法
- C++初階和進階
- 《深入理解計算機操作系統》
- 《高質量C/C++編程》
- Linux
?? 博主碼云gitee鏈接:代碼倉庫地址
?若有幫助可以【關注+點贊+收藏】,大家一起進步!
💙系列文章💙
【初階與進階C++詳解】第一篇:C++入門知識必備
【初階與進階C++詳解】第二篇:C&&C++互相調用(創建靜態庫)并保護加密源文件
【初階與進階C++詳解】第三篇:類和對象上(類和this指針)
【初階與進階C++詳解】第四篇:類和對象中(類的六個默認成員函數)
【初階與進階C++詳解】第五篇:類和對象下(構造+static+友元+內部類
【初階與進階C++詳解】第六篇:C&C++內存管理(動態內存分布+內存管理+new&delete)
【初階與進階C++詳解】第七篇:模板初階(泛型編程+函數模板+類模板+模板特化+模板分離編譯)
【初階與進階C++詳解】第八篇:string類(標準庫string類+string類模擬實現)
【初階與進階C++詳解】第九篇:vector(vector接口介紹+vector模擬實現+vector迭代器區間構造/拷貝構造/賦值)
【初階與進階C++詳解】第十篇:list(list接口介紹和使用+list模擬實現+反向迭代器和迭代器適配)
【初階與進階C++詳解】第十一篇:stack+queue+priority_queue+deque(仿函數)
【初階與進階C++詳解】第十二篇:模板進階(函數模板特化+類模板特化+模板分離編譯)
【初階與進階C++詳解】第十三篇:繼承(菱形繼承+菱形虛擬繼承+組合)
【初階與進階C++詳解】第十四篇:多態(虛函數+重寫(覆蓋)+抽象類+單繼承和多繼承)
【初階與進階C++詳解】第十五篇:二叉樹搜索樹(操作+實現+應用KVL+性能+習題)
【初階與進階C++詳解】第十六篇:AVL樹-平衡搜索二叉樹(定義+插入+旋轉+驗證)
【初階與進階C++詳解】第十七篇:紅黑樹(插入+驗證+查找)
【初階與進階C++詳解】第十八篇:map_set(map_set使用+multiset_multimap使用+模擬map_set)
【初階與進階C++詳解】第十九篇:哈希(哈希函數+哈希沖突+哈希表+哈希桶)
【初階與進階C++詳解】第二十篇:unordered_map和unordered_set(接口使用+模擬實現)
【初階與進階C++詳解】第二十一篇:哈希應用(位圖實現應用+布隆過濾器增刪查優缺點+海量數據面試題)
文章目錄
- 💙系列文章💙
- 💎前言
- 💎一、列表初始化
- 🏆1.用法
- 🏆2.initializer_list
- 💎二、變量類型推導
- 🏆1.auto
- 🏆2.decltype
- 🏆3.typeid
- 💎三、左/右值引用
- 🏆1.左值/右值引用區別
- move
- 純右值和將亡值
- 🏆2.左值引用
- 🏆3.右值引用
- 🏆4.左/右值引用的使用場景
- 移動構造/移動賦值
- 移動構造優化
- 🏆5.完美轉發
- 萬能引用
- 💎四、新增默認成員函數
- 🏆1.移動構造函數/拷貝賦值函數
- 🏆2.關鍵字default(強制生成默認)/delete(強制刪除默認)
- 💎五、可變參數模板
- 🏆1.**遞歸函數展開參數包**
- 🏆2.**逗號表達式展開參數包**
- 🏆3.**emplace_back**
- 💎六、lambda表達式
- 🏆1.lambda介紹
- 🏆2.lambda使用
- 基本使用
- 捕捉列表
- mutable
- 引用捕獲
- 傳值捕獲
- 混著捕獲
- 🏆3.優化
- 🏆4.lambda函數底層-仿函數
- 💎七、包裝器
- 🏆1.function包裝器
- 基本使用
- 🏆2.bind
💎前言
C++11是2011年更新的C++新語法,C++11能更好地用于系統開發和庫開發、語法更加泛華和簡單化、更加穩定和安全,不僅功能更強大,而且能提升程序員的開發效率。
💎一、列表初始化
🏆1.用法
使用初始化列表初始化時,可以初始化數組和自定義類型,同時可以省略=====,這些用法不常用
struct Point{int _a;int _b; }; int x1 = 1; //新寫法 int x2{ 2 }; //原來寫法 int arr1[] = { 1,2,3,4 }; //C++11支持寫法 int arr2[]{ 1,2,3,4,5,6 }; //C++11 自定義類型列表初始化 Point p{ 1, 2 };使用更多是在new(傳送門)對象的時候,初始化對象,同時還有部分自定義類型和內置類型也有新寫法
//舊寫法 int *p1 = new int(3);//開辟一個int的空間,并初始化為3賦值給p1 int *p2 = new int[3];//開辟3個int的空間,不進行初始化 //C++11寫法,用多個花括號的方式進行批量初始化 Point* p4 = new Point(1, 3); Point* p4 = new Point[2]{ {1, 2}, {3, 4} }; //下面第一二種寫法相同,利用{}直接調用構造函數 //第一種是舊寫法,第二三種是新寫法 Point p1(1, 3); Point p2{1, 3}; Point p3 = { 1, 3 };//對類來說,會進行隱式類型轉換+調用構造函數 //內置類型也可以這樣寫 vector<int> v1 = { 1,2,3,4 }; vector<int> v2{ 1,2,3,4 };//不建議這么寫🏆2.initializer_list
initializer_list一般是作為構造函數的參數(本質就是{}),C++11對STL中的不少容器就增加initializer_list作為參數的構造函數,支持多個對象初始化
auto il = { 10, 20, 30 }; cout << typeid(il).name() << endl;//initializer_list模擬實現:初始化,同時開對應size()大小空間
//initializer_list構造 vector(const initializer_list<T>& il):_start(nullptr),_finish(nullptr),_endofstorage(nullptr) {reserve(il.size());//擴容for (auto e : il) {push_back(e);} }💎二、變量類型推導
🏆1.auto
auto聲明的類型必須要進行初始化,否則編譯器無法推導出auto的實際類型。auto不能作為函數的參數和返回值,且不能用來直接聲明數組類型(傳送門)
int a = 10; auto pa = &a;🏆2.decltype
decltype是根據表達式的實際類型推演出定義變量時所用的類型。且還可以使用推導出來的類型進行變量聲明。
decltype不可以作為函數的參數,編譯時推導類型
int Add(int x, int y) {return x + y; }int a = 10, b = 20;int sum = a * b;decltype(a + b) num;// 用decltype自動推演sum的實際類型,輸出intcout << "type(sum) " << typeid(sum).name() << endl;// 用decltype自動推演a+b的實際類型,輸出intcout << "type(a+b) " << typeid(num).name() << endl;// 不帶參數,推導函數類型,輸出intcout << "type(&sum) " << typeid(decltype(Add)).name() << endl;🏆3.typeid
typeid只能查看類型,不能用其結果類定義類型,typeid只能推導類型,但是不能使用類型聲明和定義變量
int a = 9; cout << "type(a) " << typeid(a).name() << endl;💎三、左/右值引用
🏆1.左值/右值引用區別
左值:
- 一個表示數據的表達式,可以取地址和賦值,左值引用不能引用右值,左值可以出現在賦值符號的左邊,也可以出現在賦值符號的右邊
- const修飾后的左值不可以賦值,但是可以取地址,可以引用左值也可以引用右值
- 左值引用用符號&
右值:
-
一個表示數據的表達式,右值不能取地址,右值引用不能引用左值(可以move后引用左值),右值可以出現在賦值符號的右邊,但是不能出現出現在賦值符號的左邊比如:表達式返回值A+B、字面常量10
-
如果表達式運行結果或單個變量是一個引用,則認為是右值
-
右值引用的符號是&&
總結:左值可以取地址,右值不能取地址
move
move:當需要用右值引用引用一個左值時,可以通過move來獲得綁定到左值上的右值引,可以把左值換成右值,但不能把右值轉左值,則可能資源被掠奪導致該對象失效
-
被轉化的左值,其生命周期并沒有隨著左值的轉化而改變,即std::move轉化的左值變量value不會被銷毀。move告訴編譯器:我們有?個左值,但我們希望像?個右值?樣處理它。
-
STL中也有另一個move函數,就是將一個范圍中的元素搬移到另一個位置。
int main() {String s1("123");//輸出移動拷貝,s1變成一個右值,所以會調用移動構造去構造s2,這樣s1的資源就被轉移給了s2,s1本身也沒有資源了String s2(move(s1));return 0; }
純右值和將亡值
- 純右值:基本類型的常量或臨時對象,如:a+b,字面常量
- 將亡值:自定義類型的臨時對象用完自動完成析構,如:函數以值的方式返回一個對象
🏆2.左值引用
左值引用就是對左值的引用,針對出了作用域不會銷毀的變量進行引用返回,引用傳參,減少形參拷貝代價
int b = 1;const int c = 2;// 以下是對上面左值的 左值引用int& rb = b;const int& rc = c;🏆3.右值引用
下面是常見的右值和右值引用,如果表達式運行結果或單個變量是一個引用,則認為是右值
int Add(int x, int y) {return x + y; }double x = 1.1, y = 2.2; // 常見的右值 10; x + y; Add(x, y); // 以下都是對右值的右值引用 int&& rr1 = 10; double&& rr2 = x + y; int&& ret = Add(3, 4);// 函數的返回值是一個臨時變量,是一個右值不能直接對右值進行取地址/賦值操作,但是在右值引用過后,便可以對引用值進行取地址/賦值操作,給右值取別名后,會導致右值被存儲到特定位置,且可以取到該位置的地址
int&& rr1 = 1; cout << rr1 << endl; rr1 = 2; cout << rr1 << endl; int* p = &rr1; *p = 3; cout << rr1 << endl; //上面依次輸出1 2 3🏆4.左/右值引用的使用場景
左值引用:做參數和做返回值都可以提高效率
右值引用:提高移動構造/移動賦值等深拷貝場景的效率
移動構造/移動賦值
右值引用的構造函數/賦值重載稱作移動構造/移動賦值,直接拿取對象資源,避免多次拷貝造成時間和空間的損失
調用下面這個string類,下面會輸出三次深拷貝,造成浪費,如果使用左值引用,出了函數作用域之后,臨時對象會被銷毀,進行賦值的時候會出現利用str別名拷貝,但str是一個已經銷毀的對象
class String { public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);} void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);} // 移動構造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 資源轉移" << endl;swap(s);}// 拷貝構造string(const string& s):_str(nullptr), _size(0), _capacity(0){cout << "string(const string& s) -- 深拷貝" << endl;string tmp(s._str);swap(tmp);}// 移動賦值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 資源轉移" << endl;swap(s);return *this;}// 賦值重載string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷貝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];} private:char* _str;size_t _size;size_t _capacity; // 不包含最后做標識的\0 }; String func(String& str) {String tmp(str);return tmp; } int main() {String s1("123");String s2(s1);String s3(func(s1));return 0; }因為返回的臨時對象是一個右值,所以會調用上面的移動構造的代碼對返回的臨時對象進行構造,本質是資源進行轉移,此時tmp指向的是一塊空的資源。最后返回的臨時對象拷貝構造給s3時還是調用了移動構造,兩次移動構造被編譯器優化為一個。可以看出的是這里解決的是減少接受函數返回對象時帶來的拷貝,極大地提高了效率。
// 移動構造 string(string&& s) :_str(nullptr), _size(0), _capacity(0) {// 對于將亡值,內部做移動拷貝cout << "string(string&& s) -- 資源轉移" << endl;swap(s); }可以看出的是func返回的臨時對象是通過移動賦值給s2的,也是一次移動賦值,復用了之前已經寫好的一個swap函數,實現了一個“現代寫法”的構造,直接交換了二者的資源。避免深拷貝帶來的副作用
// 移動賦值 string& operator=(string&& s) { cout << "string& operator=(string&& s) -- 資源轉移" << endl; swap(s);return *this; }移動構造優化
🏆5.完美轉發
萬能引用
模板中的&&不代表右值引用,而是萬能引用,其既能接收左值又能接收右值,傳入的值,不管是左值還是右值,傳入后,都被當作左值。
void fun(int &x){ cout << "左值引用" << endl; } void fun(const int &x){ cout << "const 左值引用" << endl; }void fun(int &&x){ cout << "右值引用" << endl; } void fun(const int &&x){ cout << "const 右值引用" << endl; }// 模板中的&&不代表右值引用,而是萬能引用,其既能接收左值又能接收右值。 // 模板的萬能引用只是提供了能夠接收同時接收左值引用和右值引用的能力, // 但是引用類型的唯一作用就是限制了接收的類型,后續使用中都退化成了左值,template<typename T> void perfectforward(T&& t) {fun(t);//fun(std::move(t));//利用move將左值轉換成右值//Fun(std::forward<T>(t));//完美轉發 }int main() {perfectforward(10); // 右值int a;perfectforward(a); // 左值perfectforward(std::move(a)); // 右值const int b = 8;perfectforward(b); // const 左值perfectforward(std::move(b)); // const 右值return 0; }問題:我們此時想調用右值怎么辦,但是我們不能用move來解決問題,使用move變成右值后,數據容易被拿走
解決:使用完美轉發能夠在傳遞過程中保持它的左值或者右值的屬性,使用forward函數進行完美轉發
template<typename T> void PerfectForward(T&& t){Fun(std::forward<T>(t)); }💎四、新增默認成員函數
🏆1.移動構造函數/拷貝賦值函數
-
構造函數
-
析構函數
-
拷貝構造函數
-
拷貝賦值重載
-
取地址重載
-
const 取地址重載
-
移動構造函數
-
拷貝賦值函數
最后兩個是新增的,具有以下特點
-
如果沒有實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個。那么編譯器會自動生成一個默認移動構造。
-
默認生成的移動構造函數,對于內置類型成員會按照字節序進行淺拷貝,自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造。
-
如果沒有實現移動賦值重載函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個,那么編譯器會自動生成一個默認移動賦值。
-
默認生成的移動構造函數,對于內置類型成員會按照字節序進行淺拷貝,自定義類型成員,則需要看這個成員是否實現移動賦值,如果實現了就調用移動賦值,沒有實現就調用拷貝賦值。
class Test{ private:string _s;int _a; public:Test(const string& s="", int a=0):_s(s),_a(a){} }; void DefaultMoveCopy(){Test t1;//構造 拷貝構造 構造cout << endl;TestB t2 = t1;//拷貝構造 構造cout << endl;TestB t3 = std::move(t1); //移動拷貝cout << endl;TestB t4;t4 = std::move(t2);//構造 拷貝構造 構造 移動賦值重載cout << endl; }
-
🏆2.關鍵字default(強制生成默認)/delete(強制刪除默認)
調用對應強制生成或者刪除默認成員函數
class Person { public:Person(const char* name = "", int age = 0):_name(name),_age(age){}Person(Person&& p) = default;// 強制生成默認的Person& operator=(Person&& p) = default;Person(Person& p) = delete;// 強制刪除默認的~Person(){} private:Simulation::string _name;int _age; };💎五、可變參數模板
這就是一個可變的模板參數,允許一個函數有多個參數,且不要求是相同類型,使用sizeof即可查看參數的個數
template <class... Args> void emplace_back (Args&&... args){// 參數個數cout << sizeof...(args) << endl; }- Args和args前面有省略號,所以它們是可變參數,帶省略號的參數稱為“參數包”,它里面包含了0到N(N>=0)個模版參數
- Args是一個模板參數包,args是一個函數形參參數包
🏆1.遞歸函數展開參數包
//一個無參的同名函數,用作參數包遞歸的結尾,當參數包中的參數個數為0時,調用無參的ShowList void ShowList() {} //當參數包中只有一個參數的時候,調用對應的單參函數 //args中第一個參數作為val傳參,參數包中剩下的參數作為新的參數包傳參 template <class T> void ShowList(const T & val) { cout << val << "->" << typeid(val).name() << " end" << endl; }template <class T, class ...Args> void ShowList(const T & val, Args... args) { cout << "參數個數" << sizeof...(args) << endl; cout << val << "->" << typeid(val).name() << endl; // 遞歸調用ShowList,當參數包中的參數個數為0時,調用上面無參的ShowList ShowList(args...); } void test() { ShowList(1, 'x', string("123")); }🏆2.逗號表達式展開參數包
如果一個參數包中都是一個的類型,那么可以使用該參數包對數組進行列表初始化,參數包會展開,然后對數組進行初始化,可以利用列表初始化數組時,展開參數包的特性,再與一個逗號表達式結合使用,可以展開都會表達式中的參數.
template <class T> int PrintArg(const T& t) {cout << t << " ";return 0; }template <class ...Args> void ShowList(Args... args) {//錯誤寫法//只適用于所有參數都是相同類型的情況,如果是不同類型則會報錯//int arr[] = { args... };int arr[] = { PrintArg(args)... };cout << endl; }//都會表達式會按順序執行,先執行第一個函數,然后把最后的0值賦給數組,這樣就把參數包展開了 //展開 //(PrintArg(arg1), 0),(PrintArg(arg2), 0),(PrintArg(arg3), 0),(PrintArg(arg4), 0)🏆3.emplace_back
emplace_back可以支持可變參數包,然后調用定位new,使用參數包對空間進行初始化
template <class... Args> //萬能引用,實參是左值,參數包的這個形參就是左值引用,實參是右值,參數包的這個形參就是右值引用 void emplace_back (Args&&... args); //靈活使用 list<pair<int, string>> lt; lt.emplace_back(1, "hehe");push_back接受的是一個參數包,參數不能夠匹配,所以無法使用
list<pair<int, string>> lt; lt.push_back({ 2, "haha" });- 對于右值對象,emplace_back是先構造一個對象,移動構造,push_back也是如此
- 對于左值對象,都是先構造一個左值對象,再拷貝構造
- 對于參數包,emplace_back直接使用參數包用定位new構造
💎六、lambda表達式
對于自定義類型排序,我們可以用sort,并且可以通過仿函數使用自定義類型比較
#include <algorithm>//sort #include <functional>//greater/lessint main() {int arr[]={1,3,2,5,4}; int sz=sizeof(arr)/sizeof(arr[0]); //默認升序std::sort(arr,arr+sz); //降序傳入仿函數greater std::sort(arr,arr+sz,greater<int>()); }對于自定義類型,我們需要自己實現一個對應的仿函數
struct Goods {string _name; // 名字double _price; // 價格int _evaluate; // 評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){} };//價格降序 struct CompPriceGreater{bool operator()(const Goods& g1, const Goods& g2){return g1._price > g2._price;} }; //價格升序 struct CompPriceLess {bool operator()(const Goods& g1, const Goods& g2){return g1._price < g2._price;} }; int main () {sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());}處理的對象有很多不同的成員變量的時候,就需要實現非常非常多的仿函數
🏆1.lambda介紹
[capture-list](parameters)mutable -> return-type{statement}- [capture-list]捕捉列表,用于編譯器判斷是否是lambda表達式,同時捕捉該表達式所在域的變量以供函數使用
- (parameters)參數,和函數的參數一致。如果不需要傳參則可連帶()一起省略
- mutable默認情況下捕捉列表捕捉的參數是const修飾的,該關鍵字的作用是取消const使其可修改
- -> return-type函數返回值類型,返回類型明確的情況下可以省略,由編譯器自動推導
- {statement}函數體。除了可以使用傳入的參數,還可以使用捕捉列表獲取的參數
🏆2.lambda使用
基本使用
下面可以看到,可以省略函數返回值類型,編譯器會自動推導,或者我們可以指定返回值類型
int x = 1, y = 2; auto Add = [](int x, int y) {return x + y; }; //auto Add = [](int x, int y) ->int{return x + y; }; cout<< Add(x, y)<<endl;//輸出3捕捉列表
捕捉了函數作用域里面的局部變量x/y,直接在lambda表達式內部使用,因為不需要傳入參數,所以直接把參數()和返回值類型一并省略掉
int x = 1, y = 2; auto Add = [x, y]{return x+y }; cout<< Add(x, y)<<endl;//輸出3-
[var]:表示值傳遞方式捕捉變量var
-
[=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
-
[&var]:表示引用傳遞捕捉變量var
-
[&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
注意:lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會報錯,lambda函數生成的是一個對象,lambda表達式之間不能相互賦值
//lambda表達式之間不能相互賦值,編譯器會將他們是別成其他類型auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };//f1 = f2;
mutable
下面這個函數不加mutable是無法使用的,因為參數默認是const類型,如果不用()mutable我們不能對其修改
//mutable 只是讓傳值捕捉變量const屬性去掉了,這個關鍵字使用的時候必須帶上函數參數的() auto Swap2 = [a, b]()mutable{int tmp = a;a = b;b = tmp; };引用捕獲
下面是引用方式捕獲部分和捕獲全部對象,當然可以修改參數
// 用引用的方式捕捉 auto Swap2 = [&x, &y]{ int tmp = x; x = y; y = tmp; }; int c =2, d=3, e=4, f=5, g=6, ret; // 傳引用捕捉全部對象 auto Func2 = [&]{ ret = c + d*e / f + g; };傳值捕獲
下面是傳值捕獲全部對象
int c =2, d=3, e=4, f=5, g=6, ret; // 傳值捕捉全部對象auto Func1 = [=]{ return c + d*e / f + g; };混著捕獲
下面可以用單獨引用的方式修改ret,語法上捕捉列表由多個捕獲對象組成,用逗號分割
int c =2, d=3, e=4, f=5, g=6, ret; // 混著捕捉 auto Func3 = [c, d, &ret]{ ret = c + d; }; // ret傳引用捕捉 其他全部傳值捕捉 auto Func4 = [=, &ret]{ ret = c + d*e / f + g; };🏆3.優化
對自定義價格修改如下
vector<Goods> v = { Goods( "蘋果", 2.1, 5 ), { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } };//價格升序 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){ return g1._price < g2._price; });cout << endl; //價格降序 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){ return g1._price > g2._price; });cout << endl; //名字排序 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){ return g1._evaluate < g2._evaluate; });🏆4.lambda函數底層-仿函數
lambda的底層就是把自己轉成了一個仿函數供我們調用,就是在類中重載了operator()運算符的類對象
💎七、包裝器
🏆1.function包裝器
function包裝器,也叫作適配器。C++中的function本質是一個類模板,也是一個包裝器。頭文件< functional >
基本使用
class AddClass { public:static int Addi(int a, int b) {return a + b;}double Addd(double a, double b) {return a + b;} };int func(int a, int b) {return a + b; } //仿函數 struct Functor {int operator()(int a, int b) {return a + b;} };void TestFunction1() {// 普通函數function<int(int, int)> func1 = func;cout << func1(10, 20) << endl;// 仿函數function<int(int, int)> func2 = Functor();cout << func2(10, 20) << endl;// 類中static成員函數,,可加&可不加function<int(int, int)> func3 = AddClass::Addi;cout << func3(100, 200) << endl;// 類中非靜態成員函數,要加上&,要AddClass()的匿名對象來適配包裝器function<double(AddClass, double, double)> func4 = &AddClass::Addd;cout << func4(AddClass(), 100.11, 200.11) << endl;// lambda表達式function<int(int, int)> func5 = [](int a, int b) {return a + b; };cout << func5(100, 200) << endl; }🏆2.bind
在頭文件< functional >中,函數適配器,接受一個可調用對象,生成一個新的可調用對象配對原參數列表,現參數順序調整
auto newCallable = bind(callable,arg_list);-
newCallable本身是一個可調用對象
-
arg_list是一個逗號分隔的參數列表 ,對應給定的callable的參數
-
當我們調用newCallable時,newCallable會調用callable,并傳給它arg_list中的參數
arg_list中的參數可能包含形如_n的名字,其中n是一個整數,這些參數是“占位符”,表示newCallable的參數,它們占據了傳遞給newCallable的參數的“位置”。數值n表示生成的可調用對象中參數的位置:_1為newCallable的第一個參數,_2為第二個參數
placeholders是用來占位的,代表這里的參數需要用戶手動傳入,而_1代表傳入的第一個參數,_2就是傳入的第二個參數,同時可以利用 placeholders 調整參數個數
// 調整可調用對象的參數個數和順序 // _1 _2 ... 表示你要自己傳的那些參數,_1表示第一個參數傳給_1 // 2個參數 function<int(int, int)> func1 = bind(&AddClass::Addii,AddClass(), placeholders::_1, placeholders::_2); cout << func1(100, 200) << endl;//輸出300 // 1個參數 function<int(int)> func2 = bind(&AddClass::Addii, AddClass(), 10, placeholders::_1); cout << func1(100, 200) << endl;//輸出210總結
以上是生活随笔為你收集整理的【初阶与进阶C++详解】第二十二篇:C++11新特性(列表初始化+变量类型推到+右值引用+新增默认成员函数+可变模板参数+lambda表达式+包装器function_bind)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Studio中如何隐藏顶
- 下一篇: 调皮捣蛋的孩子--十大负面测试用例