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