上篇主要介绍一些零散的改动、类、右值引用与构造函数
下篇主要介绍Lambda函数、可变模板参数和包装器
lambda函数
在了解lambda函数前,先看一个小程序,该程序分别使用仿函数和函数指针求出一个随机区间的数有多少能被3整除
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class P
{
private:
int a;
public:
P() : a(3) {}
bool operator()(int x) const
{
return x % a == 0;
}
};
bool fun(int x)
{
return x % 3 == 0;
}
int main()
{
vector<int> vec(30);
generate(vec.begin(), vec.end(),rand);
cout << count_if(vec.begin(), vec.end(),fun) << endl;
cout << count_if(vec.begin(), vec.end(),P());
return 0;
}
然后再来说lambda函数表达式,别看他的名字比较高级,实际上它就是一个匿名函数表达式。
把普通的函数名称用[]代替,去掉其返回值,就是一个lambda函数。
有趣的是,下面的代码是在主函数中写的。
int (*add) (int,int) = [](int x,int y) { return x+y; };
cout << add(1,2);
输出的结果就是3
一些细节
返回值是使用decltype推断出来的
当且仅当只有一个返回语句时才用decltype推断,否者要使用返回类型后置语法,没有返回值decltype推断为void
可将其用于STL算法
演示,书接上文的程序
int main()
{
vector<int> vec(30);
generate(vec.begin(), vec.end(),rand);
cout << count_if(vec.begin(), vec.end(),fun) << endl;
cout << count_if(vec.begin(), vec.end(),P()) << endl;
cout << count_if(vec.begin(), vec.end(),[](int x){return x%3 == 0;});
return 0;
}
cout << count_if(vec.begin(), vec.end(),[](int x){return x%3 == 0;});
运行结果
使用lambda的好处
C++引入lambda表达式的主要目的是,能够将 类似于函数的表达式 用作 接受函数指针或者仿函数 的参数
1.使用即定义,使代码阅读方便,修改代码方便
2. lambda函数表达式更加简洁
3.效率方面,函数指针通常阻止函数内联,但是lambda表达式不会
4.比普通函数更加强大的功能,放在下面说
lambda额外的功能
lambad可访问作用域内的任何动态变量,写在前面的[]中即可,还有一些修饰的功能
[=]按值访问
[&]引用访问
[=,&x] 对x引用访问,其余动态变量按值访问
[&,x] 对x按值访问,其余动态变量引用访问 //注意没有等号
演示
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int sum = 0;
vector <int> vec {1,2,3,4,5,6,7,8,9};
for_each(vec.begin(), vec.end(),
[&sum](int x){sum+=x;});
cout << sum;
return 0;
}
输出结果45,这样做很便捷的求出了容器中的和,而不是用for历遍
包装器
包装器也叫适配器
模板的低效
//from C++ Primer Plus
#include <iostream>
#include <cmath>
using namespace std;
template <typename T, typename F>
T use_f(T v, F f)
{
static int count = 0;
count++;
cout << " use_f count = " << count << ", &count = " << &count << endl;
return f(v);
}
class Fp
{
private:
double z_;
public:
Fp(double z = 1.0) : z_(z) {}
double operator()(double p) { return z_*p; }
};
class Fq
{
private:
double z_;
public:
Fq(double z = 1.0) : z_(z) {}
double operator()(double q) { return z_+ q; }
};
double dub(double x) {return 2.0*x;}
double Sqrt(double x) {return sqrt(x);}
int main()
{
double y = 1.21;
cout << "Function pointer dub:\n";
cout << " " << use_f(y, dub) << endl;
cout << "Function pointer sqrt:\n";
cout << " " << use_f(y, Sqrt) << endl;
cout << "Function object Fp:\n";
cout << " " << use_f(y, Fp(5.0)) << endl;
cout << "Function object Fq:\n";
cout << " " << use_f(y, Fq(5.0)) << endl;
cout << "Lambda expression 1:\n";
cout << " " << use_f(y, [](double u) {return u*u;}) << endl;
cout << "Lambda expresson 2:\n";
cout << " " << use_f(y, [](double u) {return u+u/2.0;}) << endl;
cin.get();
return 0;
}
运行结果
可以看见,设置的静态变量只自增了一次,剩下都是每一次实例化一个出来,这样的结果是程序的低效
模板function是在<functional>中声明的,其从特征标的角度定义了一个对象,可用于包装特征标相同的函数指针,函数对象或者是lambda表达式。
可对上面的每一个函数进行包装
//wrapped1.cpp -- using a function wrapper as an argument
//from C++ Primer Plus
#include <iostream>
#include <math.h>
#include <functional>
using namespace std;
template <typename T, typename F>
T use_f(T v, F f)
{
static int count = 0;
count++;
cout << " use_f count = " << count << ", &count = " << &count << endl;
return f(v);
}
class Fp
{
private:
double z_;
public:
Fp(double z = 1.0) : z_(z) {}
double operator()(double p) { return z_*p; }
};
class Fq
{
private:
double z_;
public:
Fq(double z = 1.0) : z_(z) {}
double operator()(double q) { return z_+ q; }
};
double dub(double x) {return 2.0*x;}
int main()
{
double y = 1.21;
function<double(double)> ef1 = dub;
function<double(double)> ef2 = sqrt;
function<double(double)> ef3 = Fq(10.0);
function<double(double)> ef4 = Fp(10.0);
function<double(double)> ef5 = [](double u) {return u*u;};
function<double(double)> ef6 = [](double u) {return u+u/2.0;};
cout << "Function pointer dub:\n";
cout << " " << use_f(y, ef1) << endl;
cout << "Function pointer sqrt:\n";
cout << " " << use_f(y, ef2) << endl;
cout << "Function object Fp:\n";
cout << " " << use_f(y, ef3) << endl;
cout << "Function object Fq:\n";
cout << " " << use_f(y, ef4) << endl;
cout << "Lambda expression 1:\n";
cout << " " << use_f(y, ef5) << endl;
cout << "Lambda expression 2:\n";
cout << " " << use_f(y,ef6) << endl;
// cin.get();
return 0;
}
运行结果
静态变量地址一样,模板函数只被实例化了一次
其他方式
可用typedef给一个包装器起别名,用构造函数生成临时的包装器调用,达到代码复用的效果。
可将包装器当作模板参数
可变参数模板
可变模板参数说白了就是模板类中的参数不是固定的
一些关键词
模板参数包 Args
函数参数包 args
展开参数包 args...
递归
假设要写一个函数,将器传入的参数前加上**打印,我们就可以用到上面的技术
C++11提供了一个元运算符 ... ,能够用其表示模板参数包的标识符,模板参数包基本上就是一个类型列表
template <typename... Args>
void showPlus(Args... args)
{
//...
}
当然,想要一次显示内容还需要进行参数包展开,以递归的形式
首先来看一展开的语法,将省略号放在参数名的右面
template <typename... Args>
void showPlus(Args... args)
{
showPlus(args...); //该函数调用和原函数调用一样
//因此,这样讲导致递归,进行函数参数包的无限展开
}
但是这将导致无限递归,这样改写
using namespace std;
void showPlus() {}// 0 参数 —— 终止调用,定义额外的版本当作基线
template <typename T, typename... Args>
void showPlus(T &t, Args... args)
{
cout << "**" << t <<endl;
showPlus(args...);
}
int main()
{
int a =3;
double b = 3.14;
string s ="hello";
showPlus(a,b,s);
return 0;
}
也可以单独定义一个一个参数的模板当作递归基线,可进行最后出书格式的调整