std::initializer_list<T>{ };
可以使用列表初始化來進行初始化,T表示的是參數類型,initializer_list可以被拷貝,但是一定要注意的是,它是一種引用拷貝,也就是說拷貝后新的list和被拷貝的list是的元素都是共享的。
2. 第二種是省略符形參,熟悉C的人對這個也應該很熟悉了,就是printf和scanf這些函數所用的方法。
void print(int, ...);
省略符形參其實是為了方便訪問特殊的C代碼而設計的,這些代碼其實是使用了標准庫varargs的功能(C標准庫)。
1 #include <stdarg.h> 2 void method(int i, ...) 3 { 4 int v; 5 va_list params;//聲明可變參數列表 6 va_start(params, i); //依據可變參數前的參數獲得可變參數的起始地址 7 do { 8 v = va_arg(params, int); //從可變參數列表中獲得參數的值,類型為int 9 printf("%d", v); 10 } while (v != 0); 11 va_end(params); 12 } 13 14 void format(const char* format, ...) 15 { 16 char buf[1024];va_list params; 17 va_start(params, format); 18 vsnprintf_s(buf, 1024, 1024, format, params); 19 va_end(params); 20 printf("%s", buf); 21 }
typedef int arr[10];//typedef方法 using Arr = int[10];//別名用法 Arr *func0(int i); int(*func1(int i))[10]; auto func2(int i) -> int(*)[10];
1 //尾置返回類型(明確知道函數返回的指針指向什么地方) 2 int odd[] = { 1,3,5,7,9 }; 3 int even[] = { 0,2,4,6,8 }; 4 5 decltype(odd) *arrPtr(int i) 6 { 7 return (i % 2) ? &odd : &even; 8 }
1 const std::string &shortString(const std::string &s1, const std::string &s2) 2 { 3 return s1.size() <= s2.size() ? s1 : s2; 4 } 5 std::string &shortString(std::string &s1, std::string &s2) 6 { 7 auto &r = shortString(const_cast<const std::string &>(s1), 8 const_cast<const std::string &>(s2));//注意這里的shortSring必須要const_cast 9 //因為在這里s1和s2都是非常連版本,而編譯器會優先選擇非常量版本進行調用,導致遞歸出錯 10 return const_cast<std::string &>(r); 11 }
const_cast和重載(注意自己一定要清楚const_cast之前的變量是不是const,如果是const,那么就會產生未定義的行為)。
1 size_t wd = 80;char def = ' '; 2 size_t ht(); 3 string screen(size_t = ht(), size_t = wd, char = def); 4 string window = screen();//調用screen(ht(), 80 ' ') 5 6 void f2() 7 { 8 def = '*'; 9 size_t wd = 100; 10 window = screen();//調用screen(ht(), 80 '*') 11 }
1 constexpr size_t scale(size_t cnt) 2 { 3 using test = int; 4 return cnt * 2; 5 }
int t; int arr[scale(100)]; auto ret = scale(t);
scale(100)返回的是一個常量表達式,但是scale(t)則返回的就是size_t類型。
- 精確匹配(實參類型和形參類型是一樣的,或者實參從數組類型或者函數類型轉換成對應的指針類型,向實參添加頂層const或者從實參中消除頂層const)(對於底層const,如果同時定義了非常量版本和常量版本,如果傳入非常量,那么就會調用非常量版本函數,如果傳入的實參為常量版本,則會調用常量版本的函數)。
- 通過const實現的匹配
- 通過類型提升來實現的匹配(比如整型提升)。
- 通過算術轉換或者指針轉換實現的匹配。
- 通過類類型轉來來匹配的轉換。
int(*pf)(std::string s1);
1 int test(std::string s1) { return 0; } 2 int(*pf)(std::string s1); 3 pf = test; 4 pf = &test; 5 test(std::string()); 6 (*test)(std::string());
auto foo() -> int(*)(std::string s1);
decltype(test) *getfcn();
3. lambda表達式(匿名函數對象,一個C++的語法糖)
一個lambda表達式表示一個可調用的代碼單元,我們可以將其理解為有一個未命名的內聯函數,與任何函數類似,一個lambda具有一個返回類型,一個參數列表和一個函數體,並且這個函數體是可以定義在函數內部的。
auto fcn = [] {return 42;};//定義了一個可調用對象fcn為一個lambda表達式 //忽略參數列表,忽略返回類型(都為空)
1 #include <algorithm> 2 #include <iostream> 3 #include <vector> 4 5 using std::vector; 6 using std::find_if; 7 using std::for_each; 8 using std::cout; 9 using std::endl; 10 11 void searchSegment(vector<int> nums, const int floorSize, const int ceilSize) 12 { 13 std::stable_sort(nums.begin(), nums.end(), 14 [](const int &a, const int &b) { return a < b;});//注意sort和stable_sort當comp(x,x)的時候一定要返回false 15 auto segFloorIndex = find_if(nums.begin(), nums.end(), 16 [&floorSize](const int &a) ->bool { return a >= floorSize; }); 17 auto segCeilIndex = find_if(nums.begin(), nums.end(), 18 [&](const int &a) { return a >= ceilSize; }); 19 cout << "The size of the segment is " << segCeilIndex - segFloorIndex << endl; 20 }
捕獲分值捕獲和引用捕獲,引用捕獲就在變量前面加&就可以了,引用捕獲可以解決某些類不能被拷貝的問題(比如輸入輸出流),另外捕獲還可以是隱式的,比如我上面的算segCeilIndex的時候,就可以直接一個&就代表捕獲所有的引用參數了。
int v; auto f = [v]()mutable {return ++v; };
我們上面看到lambda表達式可以很好地解決標准庫有些函數的謂詞問題,現在我們想問的問題是,如果不想用lambda來實現謂詞的轉換,我只想用函數,那么怎么辦呢?標准庫提供了一個很好的辦法,那就是bind函數(C++11 Feature)。(定義在頭文件functional里面)。
1 bool findMin(const int &a, const int &value) 2 { 3 return a >= value; 4 } 5 segFloorIndex = find_if(nums.begin(), nums.end(), bind(findMin, _1, floorSize)); 6 segCeilIndex = find_if(nums.begin(), nums.end(), bind(findMin, _1, ceilSize));
其中_1(類似的還有_2,_3....)這個東西為占位符(有點像匯編的那個,定義在std的placeholders的作用域),表示的是外面的可以傳入的參數,參數從左到右依次填入_1,_2...中(比如如果有調用對象G的定義為auto G= bind(f, _2, a, _1 ),調用G(x,y)實際上是f(Y,a,x ),用這個拿來交換參數位置)。
C++的類厲害的地方之一就是可以重載函數運算,就是retType operator( )(parameter...){ }的形式,這種重載了函數調用符的類可以直接讓我們用類名來實現函數的功能,比如:
1 class FunctonalClass 2 { 3 public: 4 bool operator()(const int &a, const int &value) 5 { 6 return a >= value; 7 } 8 };
1 class AnonymityFunctional 2 { 3 public: 4 AnonymityFunctional(const int &v) :value(v) { } 5 bool operator()(const int &a) const { return a >= value; } 6 private: 7 const int &value; 8 };
segFloorIndex = find_if(nums.begin(), nums.end(), AnonymityFunctional(floorSize));
segCeilIndex = find_if(nums.begin(), nums.end(), AnonymityFunctional(ceilSize));
上面說了5種可調用對象,有時候我們想實現某些設計模式的時候,我們想用一個統一接口來調用這些對象,實現可調用對象表,但是這些可調用對象本質上類型都是不一樣的,如何把他們統一起來呢?答案就是使用標准庫模板function<T>,
std::function<bool(const int &)>
1 bool fcn(const int &a){ return a; } 2 3 class FunctionTest 4 { 5 public: 6 FunctionTest() = default; 7 8 bool getWhat(const int &a)const { return true; } 9 bool getS()const { return true; } 10 }; 11 12 std::function<bool(const int &)> f1 = [](const int &a) { return a ; }; 13 std::function<bool(const int &)> f2 = Test(); 14 std::function<bool(const int &)> f3 = fcn;
最常見的就是把一些函數放進map里面,那樣我們就可以根據關鍵字來調用對象了,非常方便。不過需要注意的是,如果函數是重載過的,那我們就不能直接塞函數名進去了(會產生二義性),這個時候就要自己創建要放入函數的函數指針,再創建function。
1 bool fcn() { return true; } 2 bool fcn(const int &a){ return a; } 3 4 bool(*pfcn)(const int &) = fcn; 5 std::function<bool(const int &)> f3 = pfcn;
1 class FunctionTest 2 { 3 public: 4 FunctionTest() = default; 5 6 bool getWhat(const int &a)const { return true; } 7 bool getS()const { return true; } 8 };
1 FunctionTest haha, *phaha = &haha; 2 3 bool (FunctionTest::*pf)(const int &) const;//先定義一個指針 4 pf = &FunctionTest::getWhat;//指針指向類的什么函數 5 6 auto ret = (haha.*pf)(floorSize); 7 auto ret1 = (phaha->*pf)(ceilSize);
auto pf = &FunctionTest::getWhat;
std::function<bool(const FunctionTest *, const int &)> f4(&FunctionTest::getWhat);
這里我們發現了要生成成員函數的可調用對象必須帶對象,所以生成的可調用對象都必須帶類的對象,這很符合成員函數指針的要求——必須綁定對象,事實上,當我們使用這樣的可調用對象的時候,就相當於把this指針先傳給第一個參數了,所以這個function對象本質上是需要兩個參數的!不能用於find_if。
auto f = std::mem_fn(&FunctionTest::getWhat);