《C++新经典》第20章 高级话题与新标准
admin
2024-03-26 12:41:27
0

《C++新经典》第20章 高级话题与新标准

  • 20.1 函数调用运算符与function类模板
    • 20.1.2 函数调用运算符
    • 20.1.3 不同调用对象的相同调用形式
    • 20.1.4 标准库function类型简介
  • 20.2 万能引用
    • 20.2.1 类型区别基本概念
    • 20.2.2 universal reference基本认识
    • 20.2.3 万能引用资格的剥夺与辨认
  • 20.3 理解函数模板类型推断与查看类型推断结果
    • 20.3.1 如何查看类型推断结果
    • 20.3.2 理解函数模板类型推断
  • 20.4 引用折叠、转发、完美转发与forward
    • 20.4.1 引用折叠规则
    • 20.4.2 转发与完美转发
    • 20.4.3 std::forward
    • 20.4.4 std::move与std::forward的区别
  • 20.5 理解auto类型推断与auto应用场合
    • 20.5.1 auto类型常规推断
    • 20.5.2 auto类型针对数组和函数的推断
    • 20.5.3 auto类型std::initializer_list的特殊推断
    • 20.5.4 auto不适合场合
    • 20.5.5 auto适合场合
  • 20.6 详解decltype含义与decltype主要用途
    • 20.6.1 decltype含义和举例
    • 20.6.2 decltype主要用途
  • 20.7 可调用对象、std::function与std::bind
    • 20.7.1 可调用对象
    • 20.7.2 std::function可调用对象包装器
    • 20.7.3 std::bind绑定器
  • 20.8 lambda表达式与for_each、find_if简介
    • 20.8.1 用法简介
    • 20.8.2 捕获列表
    • 20.8.3 lambda表达式延迟调用易出错细节分析
    • 20.8.4 lambda表达式中的mutable
    • 20.8.5 lambda表达式的类型和存储
    • 20.8.6 lambda表达式再演示和优点总结
  • 20.9 lambda表达式捕获模式的陷阱分析和展示
    • 20.9.1 捕获列表中的&
    • 20.9.2 形参列表可以使用auto
    • 20.9.3 成员变量的捕获问题
    • 20.9.4 广义lambda捕获
    • 20.9.5 静态局部变量
  • 20.10 可变参数函数、initializer_list与省略号形参
    • 20.10.1 可变参数函数
    • 20.10.2 initializer_list(初始化列表)
    • 20.10.3 省略号形参(...)
  • 20.11 萃取技术概念与范例等
    • 20.11.1 类型萃取简介
    • 20.11.2 类型萃取范例

20.1 函数调用运算符与function类模板

20.1.2 函数调用运算符

若类重载了函数调用运算符(),就可以像使用函数一样使用该类的对象(像函数调用一样来调用该类的对象)。

class biggerthanzero {
public:int operator()(int value) const {if(value<0)return 0;return value;}
};int i = 200;
biggerthanzero obj;
int result = obj(i); //等价于int result = obj.operator()(i);
class biggerthanzero {
public:biggerthanzero(int i) {cout<<"biggerthanzero::biggerthanzero(int)\n";}int operator()(int value) const {if(value<0)return 0;return value;}
};int i = 200;
biggerthanzero obj(i);//对象定义并初始化,调用的是biggerthanzero构造函数
int result = obj(i); //等价于int result = obj.operator()(i);

若类重载了()运算符(可有多个版本,参数类型或数量有差别即可),则类对象就变成了可调用对象(函数对象)。

20.1.3 不同调用对象的相同调用形式

int echovalue(int value) {cout<

echovalue函数和biggerthanzero类重载的函数调用符具有相同的形参和返回值,叫做调用形式相同。

一种调用形式对应一个函数类型。
int(int)表示接收一个int参数,返回一个int值的函数类型。

可以将可调用对象(函数对象、仿函数)的指针保存起来,方便后续随时调用。

map myoper;
myoper.insert({"ev", echovalue});//系统不会将类biggerthanzero或类对象obj看成函数指针
biggerthanzero obj;
myoper.insert({"bt", biggerthanzero});//error
myoper.insert({"bt", obj});	//error

20.1.4 标准库function类型简介

function,类模板,用来包装可调用对象,统一了类型。

biggerthanzero obj;
function f1 = echovalue;
function f2 = obj;
function f3 = biggerthanzero();f1(5);
f2(3);
f3(-5);
biggerthanzero obj;
map> myoper = {{"ev", echovalue},{"bt", obj}
};
myoper.insert({"bt2", biggerthanzero()});myoper["ev"](12);
myoper["bt"](3);
myoper["bt2"](-5);

只要函数是重载的,就无法包装进function中。
可以通过定义函数指针解决。

int echovalue(int value) {cout<return;
}function f1 = echovalue; //errorint(*fp)(int) = echovalue;
function f1 = fp; //ok

20.2 万能引用

20.2.1 类型区别基本概念

void func(const int&abc){}
//abc类型为const int&
template
void func(const T&abc){}func(10);
//T(类型模板参数)的类型是int(与输入参数10和abc类型即const int &有关)
//abc(变量)的类型是const int &

20.2.2 universal reference基本认识

universal reference(forwarding reference,转发引用),万能引用(未定义引用)。

万能引用是一种类型。

void func(int &&tmp) {//tmp为右值引用类型cout<
//函数模板推断+函数形参为T&&会出现万能引用
//auto && tmp = ...也是万能引用
//实参传递左值,则万能引用是左值引用(tmp被推断为int &类型,假设传递int)
//实参传递右值,则万能引用是右值引用(tmp被推断为int &&类型,假设传递int)
//T&&(tmp)是万能引用,T不是
template
void func(T&& tmp){//tmp类型为T&&,&&和T类型无关cout<
void func(int &¶m){}//右值引用template
void func(T&& tmp){} //万能引用template
void func(vector&& tmp){} //右值引用
template
void myfunc(T&& tmp){tmp = 12;cout<

20.2.3 万能引用资格的剥夺与辨认

  1. 剥夺
//const剥夺万能引用资格,const T&&只能是右值引用。
template
void myfunc(const T&& tmp){//右值引用tmp = 12;cout<
  1. 辨认
template
class mytestc {
public:void testfunc(T&& x){}//右值引用,不是万能引用
};mytestc mc;
int i = 100;
mc.testfunc(i); //error
mc.testfunc(std::move(i)); //ok
template
class mytestc {
public:void testfunc(T&& x){}//右值引用,不是万能引用templatevoid testfunc2(T2&& x){}//万能引用
};mytestc mc;
int i = 100;
mc.testfunc2(i); //ok
mc.testfunc2(std::move(i)); //ok

20.3 理解函数模板类型推断与查看类型推断结果

20.3.1 如何查看类型推断结果

boost

#include template 
void myfunc(T& tmprv){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<

20.3.2 理解函数模板类型推断

#include template 
void myfunc(const T& tmprv){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<
  1. 指针或引用类型
#include template 
void myfunc(T& tmprv){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<
#include template 
void myfunc(const T& tmprv){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<
#include template 
void myfunc(T* tmprv){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<
  1. 万能引用类型
template 
void myfunc(T&& tmprv){...}int i = 18;
myfunc(i);
//T = int &
//tmprv = int &const int j = i;
myfunc(j);
//T = int const &
//tmprv = int const &const int& k = i;
myfunc(k);
//T = int const &
//tmprv = int const &myfunc(100);
//T = int
//tmprv = int &&
  1. 传值方式
template 
void myfunc(T tmprv){...}int i = 18;
myfunc(i);
//T = int
//tmprv = intconst int j = i;
myfunc(j);
//T = int
//tmprv = intconst int& k = i;
myfunc(k);
//T = int
//tmprv = intchar mystr[] = "test";
const char * const point = mystr;
myfunc(point);
//myfunc(mystr);//同上
//T = char const *
//tmprv = char const *
//tmprv = nullptr;//ok
//*tmprv = 'Y';//error
  1. 数组作为实参
template 
void myfunc(T tmprv){...}const char mystr[] = "test";
myfunc(mystr);
//T = char const *
//tmprv = char const *
template 
void myfunc(T& tmprv){...}const char mystr[] = "test";
myfunc(mystr);
//T = char const [5]
//tmprv = char const (&)[5]
template 
void myfunc(T (&tmprv)[L1]){...}const char mystr[] = "test";
myfunc(mystr);
//T = char const [5]
//tmprv = char const (&)[5]
  1. 函数名作为实参
template 
void myfunc(T tmprv){...}void testFunc(){}myfunc(testFunc);
//T = void(__cdecl *)(void)
//tmprv = void(__cdecl *)(void)
template 
void myfunc(T& tmprv){...}void testFunc(){}myfunc(testFunc);
//T = void __cdecl (void)
//tmprv = void(__cdecl &)(void)

20.4 引用折叠、转发、完美转发与forward

20.4.1 引用折叠规则

template
void myfunc(T&& tmprv){...}int i = 18;
myfunc(i);
//T = int &
//tmprv = int &myfunc(100);
//T = int
//tmprv = int &&
//第一组&是左值引用,第二组&&是右值引用
//左值相遇、左右值相遇、右值相遇、右左值相遇,会发生&符合的合并,折叠。
void myfunc(int& && tmprv){...}int i = 18;
myfunc(i);
//tmprv = int &
//第一组&是左值引用,第二组&&是右值引用
//左值相遇、左右值相遇、右值相遇、右左值相遇,会发生&符合的合并,折叠。
void myfunc(int& && tmprv){...}//编译器内部进行折叠(合并),程序不能写三个&&&
//int b = 500;
int& byi = b;
//int& &byy = byi;//error
//int& &byy2 = b;//error左值-左值,&-&
左值-右值,&-&&
右值-左值,&&-&
右值-右值,&&-&&
存在左值引用,结果就是左值引用。上述组合结果为:
左值引用,&
左值引用,&
左值引用,&
右值引用,&&

20.4.2 转发与完美转发

转发,把收到的参数以及这些参数相对应的类型(const、左值、右值等)不变地转发给其他函数的函数模板。

万能引用T&&函数模板的形参,可以保存实参的所有类型信息(const、引用等),编译器据此推断出函数模板最终的形参类型。

template
void myFuncTemp(F f, T1 &&t1, T2 &&t2){...}
//实参中来的左值或者右值信息、const信息都保存在t1和t2参数中
void myfunc(int v1, int& v2){++v2;cout<cout<}int j = 2;
//20右值,j左值
myFuncTemp(myfunc2, 20, j);template
void myFuncTemp(F f, T1 &&t1, T2 &&t2){f(t1, t2);//t1,t2都是左值(变量,形参本身是左值),即使t1是右值引用
}//v1中v1需要右值,myfunc2传递左值会报错
void myfunc2(int&& v1, int& v2){cout<

20.4.3 std::forward

专门为转发而存在的函数,要么返回左值,要么返回右值。
实参左值,std::forward后还是左值;
实参右值,形参变为左值,std::forward后转为右值。
std::forward能够保持原始实参的左值或者右值性。

void printInfo(int& t){cout<<"int&"<cout<<"int&&"<
void testF(T&& t){using boost::typeindex::type_id_with_cvr;cout<<"T = "<().pretty_name()<().pretty_name()<(t));printInfo(std::move(t));
}TestF(1);
/*
T = int
t = int &&
int&
int&&
int&&
*/int i = 1;
TestF(i);
/*
T = int &
t = int &
int&
int&
int&&
*/
int ix = 12;
//std::forward(ix)转为右值
//std::forward(ix)转为左值
int&& def = std::forward(ix);

完美转发,将任意的函数名、任意类型参数(个数确定)(保持参数类型不变)传递给函数模板,达到间接调用函数的目的。

20.4.4 std::move与std::forward的区别

  • std::move无条件强制转换为右值(原来左值,强制转成右值;原来右值,保持不变);std::forward某种条件下执行强制转换(原来左值,保持不变,原来右值,因为参数传递等变成左值,std::forward能转换回右值);
  • std::move只需要普通参数,std::forward需要模板类型参数和普通参数;
  • std::move左值后,不应该使用这个左值了;std::forward转发对象,保持对象的左值性或右值性。

20.5 理解auto类型推断与auto应用场合

20.5.1 auto类型常规推断

声明变量时根据变量初始值的类型自动推断匹配类型。
auto特点:

  • auto自动类型推断发生在编译期间;
  • audo定义变量必须立即初始化,才能推断auto类型和整个变量类型;
  • auto可以和指针、引用、const等结合使用。
//auto理解为类型模板参数T,x理解为模板中形参类型。
//T(auto) = int
//tmprv(x) = int
auto x = 27;
  1. 传值方式(非指针,非引用)
    auto后面直接跟变量名。
auto x = 27;//T = int, t = int
const auto x2 = x;//T = int, t = const int
const auto& xy = x;//T = int, t = const int&
auto xy2 = xy;//T = int, t = int

传值方式针对auto类型会抛弃引用、const等限定符。

  1. 指针或引用类型但不是万能引用
    auto后面接一个&,不会抛弃const限定符,但是会抛弃引用。
auto x = 27;
const auto& xy = x;//const int&auto& xy3 = xy;//const int &, auto = const int
auto y = new auto(100); // y = int*, auto = int *,auto可用于new操作符const auto* xp = &x;//const int *, auto = int
auto * xp2 = &x; //int *, auto = int
auto xp3 = &x;// int *,
  1. 万能引用类型
    auto后面接&&。
auto x = 22;
auto&& wnyy0 = 222;//int&&, auto = int
auto&& wnyy1 = x;//int&, auto = int&const auto x2 = x;//const int
auto&& wnyy3 = x2;//int const &, auto = int const &

20.5.2 auto类型针对数组和函数的推断

const char mystr[] = "test";//const char[5]
auto myarr = mystr;//char const *
auto& myarr2 = mystr;//char const(&)[5]int a[2] = {1, 2};
auto aauto = a;// int *
void myfunc3(double, int){cout<<"myfunc3"<

20.5.3 auto类型std::initializer_list的特殊推断

初始化变量方法:

int x = 10;//c++98
int x2(20);//c++98
int x3 = {30};//c++11
int x4{40};//c++11
auto x = 10;//int
auto x2(20);//c++98
auto x3 = {30};//std::initializer_list,隐式类型转换
auto x4{40};//int

std::initializer_list是c++1中类模板,数组,与vector类型。

auto x5 = {30, 21};
auto x6 = {30, 21, 45.3};//error,类型不一致
template
void fautof(T param){}fautof({12});//error
template
void fautof(std::initializer_list param){}fautof({12});//ok
//c++14支持
auto funca(){return 12;
}

20.5.4 auto不适合场合

(1)不能用于函数参数。

void myfunc(auto x, int y){}//error

(2)不能类中普通成员变量。

class CT{
public://auto m_i = 12;//errorstatic const auto m_si = 15;//ok
};

20.5.5 auto适合场合

std::map mymap;
mymap.insert({"aa", 1});
mymap.insert({"bb", 2});
mymap.insert({"cc", 3});
mymap.insert({"dd", 4});//for(std::map::iterator iter = mymap.begin(); iter != mymap.end(); ++iter)
for(auto iter = mymap.begin(); iter != mymap.end(); ++iter)cout<first<<" = "<second<
class A{
public:static int testr(){return 0;}
};class B{
public:static double testr(){return 10.5;}
};template
auto ftestclass(){auto value = T::testr();return value;
}cout<()<()<

20.6 详解decltype含义与decltype主要用途

20.6.1 decltype含义和举例

decltype用于推导表达式或者变量名的类型,与auto类型。
decltype特点:

  • decltype的自动类型推断发生在编译器,与auto一样;
  • decltype不会真正计算表达式的值;
  • const限定符、引用属性等可能会被auto抛弃,decltype一般不会抛弃任何东西。
  1. decltype后的圆括号中是变量
const int i = 0;
const int& iy = i;
auto j1 = i;//传值方式推断,引用、const等被抛弃,int
decltype(i) j2 = 15;//const int
decltype(iy) j3 = j2;//const int &
class CT{
public:int i;int j;
};decltype(CT::i) a;//int
CT tmpct;
decltype(tmpct) tmpct2;//CT
decltype(tmpct2.i) mv = 5;//int
int x = 1;
auto&& z = x;//万能引用,x左值,auto是int&,z也是int&
int y = 2;
decltype(z) &&h = y;//int & &&,折叠引用,最终int &h = y;
  1. decltype后的圆括号中是表达式
decltype(8) kkk = 5;//intint i = 0;
decltype(i) k2;//intint *pi = &i;
decltype(pi) k;//int *
*pi = 4;
//*pi是左值,*pi是表达式不是变量
//如果表达的结果能够作为赋值语句等号左侧的值(*pi=4;)
//那么decltype后返回的就是一个引用
decltype(*pi) k3 = i;//int &
//(i)表达式,i左值
//decltype((变量))的结果永远是引用
decltype((i)) iy3 = i;//int &int& iy = i;
decltype(iy+1) j;//int
  1. decltype后的圆括号中是函数
int testf(){return 10;
}decltype(testf()) tmpv = 14;//int
decltype(testf) tmpv2;//int(void),可调用对象function ftmp = testf;
cout<return 0;
}decltype(myfunctest()) myy = 0;//const int &&

20.6.2 decltype主要用途

  1. 应付可变类型
template
class CTTMP{
public:typename T::iterator iter;void getbegin(T &tmpc){iter = tmpc.begin();}
};using conttype = std::vector;
conttype myarr = {10, 30, 40};
CTTMP ct;
ct.getbegin(myarr);
//using conttype = const std::vector;//error,
常量容器定义初始化,begin等返回常量迭代器vector::const_iterator,而不是vector::iterator
c++98通过写类模板偏特化解决
template
class CTTMP{
public:typename T::const_iterator iter;void getbegin(const T &tmpc){iter = tmpc.begin();}
};
template
class CTTMP{
public:decltype(T().begin()) iter;void getbegin(T &tmpc){iter = tmpc.begin();}
};
class A{
public:A(){cout<<"A()"<cout<<"~A()"<cout<<"func()"<
  1. 通过变量表达式抽取变量类型
vector ac;
ac.push_back(1);
ac.push_back(2);vector::size_type mysize = ac.size();
cout<
  1. auto结合decltype构成返回类型后置语法
auto func(int a, int b) -> int{}
auto add(int i, int k) -> decltype(i+k) {return i + k ;
}
int& tf(int& i){return i;
}
double tf(double& d){return d;
}template
auto FuncTmp(T& tv) -> decltype(tf(tv)) {return tf(tv);
}int i = 19;
cout<
  1. decltype(auto)用法
    c++14使用。

(1)函数返回类型

template
T& mydouble(T& v){v *= 2;return v;
}
int a = 100;
mydouble(a) = 20;
cout<
template
auto mydouble(T& v){//auto推导去掉引用v *= 2;return v;
}
template
decltype(auto) mydouble(T& v){//保留引用v *= 2;return v;
}int a = 100;
decltype(mydouble(a)) b = a;//int &

(2)变量声明

int x = 1;
const int& y = 1;
auto z = y;//int
decltype(auto) z2 = y;//z2与y完全一致, const int&

(3)(变量)

int i = 10;
decltype((i)) iy3 = i;//int &
decltype(auto) tf1(){int i = 1;return i;//int
}
decltype(auto) tf2(){int i = 1;return(i);//int&
}decltype(tf1()) testa = 4;//int
int a = 1;
decltype(tf2()) testb = a;//int&
tf2() = 12;//引用值已释放

20.7 可调用对象、std::function与std::bind

20.7.1 可调用对象

  1. 函数指针
void myfunc(int tv){cout<
  1. 具有operator()成员函数的类对象(仿函数/函数对象)
    仿函数(functors)/函数对象(function objects):能行使函数功能的类所定义的对象。
class TC{
public:void operator()(int tv){cout<
  1. 可被转换为函数指针的类对象
    可被转换为函数指针的类对象也可以叫做仿函数/函数对象。
class TC2{
public:using tfpoint = void(*)(int);static void mysfunc(int tv){cout<return mysfunc;}//类型转换运算符/函数
};TC2 tc2;
//先调用tfpoint,再调用mysfunc,这就是一个可调用对象,等价于tc2.operator TC2::tfpoint()(20);
tc2(20); 
  1. 类成员函数指针
class TC{
public:void ptfunc(int tv){cout<
  1. 总结

可调用(可使用函数运算符“()”)对象,可以类似a(参数1,参数2,…)使用。

可调用对象调用形式统一(名字(参数列表)),定义方法不同。

std::function将可调用对象包装起来,统一可调用对象的调用形式。

20.7.2 std::function可调用对象包装器

  1. 绑定普通函数
void func(int v){cout< f = func;
func(10);
  1. 绑定类的静态成员函数
class TC{
public:static void func(int v){cout< f = TC::func;
f(10);
  1. 绑定仿函数
class TC{
public:void operator()(int v){cout< f = tc;
f(10);
  1. 范例
void mycallback(int cs, const std::function &f){f(cs);
}void runfunc(int x){cout<
#include 
#include 
using namespace std;class CB
{std::function fcallback;public:~CB(){cout << "CB:~CB()" << endl;}CB(const std::function &f) : fcallback(f){cout << "CB:CB(const std::function &)" << endl;}void runcallback(){fcallback();}
};class CT
{
public:~CT(){cout << "CT:~CT()" << endl;}CT(){cout << "CT:CT()" << endl;}CT(const CT &){cout << "CT::(const CT &)" << endl;}void operator()(){cout << "CT::operator()()" << endl;}
};int main()
{if (0){CT ct;const std::function &f = ct; //CT::(const CT &)std::function fcallback = f ; //CT::(const CT &)}if (1){CT ct;CB cb(ct);cb.runcallback();}cout << "Over!" << endl;return 0;
}

20.7.3 std::bind绑定器

std::bind是函数模板,取代c++98中的bind1st和bind2nd。
std::bind将对象及相关参数绑定在一起,绑定完后可以直接使用,也可以用std::function保存,需要时调用。

std::bind(待绑定的函数对象/函数指针/成员函数指针,参数绑定值1,参数绑定值2,...,参数绑定值n)

std::bind两层意思:

  • 将可调用对象和参数绑定一起,构成仿函数,可以直接调用;
  • 绑定部分参数,其他参数调用时指定。
void myfunc1(int x, int y, int z){cout<<"x="<
void myfunc2(int& x, int& y){x++;y += 2;
}int a = 2;
int b = 3;
//bind预先绑定的参数通过值传递,a值传递
//不事先绑定的参数,placeholders通过引用传递,b引用传递
auto bf4 = std::bind(myfunc2, a, placeholders::_1);
bf4(b);//
cout<
class CQ{
public:void myfunc(int x, int y){m_a = x;}int m_a = 0;
};CQ cq;
//cq会生成临时CQ对象
//&cq不会生成临时CQ对象
auto bf5 = std::bind(&CQ::myfunc, cq, placeholders::_1, placeholders::_2);
bf5(10, 20);std::function bf6 = std::bind(&CQ::myfunc, &cq, placeholders::_1, placeholders::_2);
bf6(10, 20);
class CQ{
public:CQ(){cout<<"CQ(): "<cout<<"CQ(const CQ&): "<cout<<"~CQ(): "< bf7 = std::bind(&CQ::m_a, &cq);
bf7() = 7;//cq.m_a = 7//std::function bf7 = std::bind(&CQ::m_a, cq);
//会调用两次拷贝构造函数,两次析构函数
//一次cq生成临时对象
//一次bind要返回包装后的CQ对象//会调用一次构造函数,一次拷贝构造函数,两次析构函数
auto rt = std::bind(CT());
rt();
void mycallback(int cs, const std::function &f){f(cs);
}void runfunc(int x){cout<

20.8 lambda表达式与for_each、find_if简介

20.8.1 用法简介

lambda表达式也是一种可调用对象,定义一个匿名函数,并且可以捕获一定范围内的变量。

//lambda表达式一般形式:
//[捕获列表](参数列表) -> 返回类型{函数体;};
auto f = [](int a) -> int {return a + 1;
};
cout <

lambda表达式特点:

  • 是匿名函数,可调用代码单元或者未命名的内联函数;
  • 有返回类型、参数列表、函数体;
  • 可在函数内部定义。

注意:
(1)返回类型后置或者省略(return语句推断返回值类型)。
(2)参数列表可以有默认值。

auto f = [](int a = 8) -> int {return a + 1;
};

(3)无参时,参数列表甚至’()'都可以省略。

auto f1 = [](){return 1;
};
auto f2 = []{return 1;
};

(4)捕获列表[]和函数体{}不能省略。

20.8.2 捕获列表

捕获列表捕获一定范围内的变量。
(1)[]:不捕获任何变量。

int i = 9;
auto f = []{return i;//error,无法捕获外部变量i
};
static int i = 9;
auto f = []{return i;//ok,可以直接使用局部静态变量
};

(2)[&]:捕获外部作用域中所有变量,作为引用在函数体中使用。

int i = 9;
auto f = [&]{i = 5; //&,会修改i值return i;
};

(3)[=]:捕获外部作用域中所有变量,作为副本(按值)在函数体中使用,可以使用,不能赋值(常量引用?)。

int i = 9;
auto f = [=]{//i = 5; //error,不能赋值return i;
};

(4)[this]:用于类中,捕获当前类this指针,让lanbda表达式拥有和当前类成员函数同样的访问权限。使用"&“和”="时,默认添加了此项“this”。
[this]和[=]可以读取,不能修改;[&]可以修改。

class CT{
public:int m_i = 5;void myfuncpt(int x, int y){auto mylambda = [this] {//this,&,=都可以读取成员变量值return m_i;};cout<

(5)按值捕获和按引用捕获。

  • [变量名]:按值捕获(不能修改)变量,不捕获其他变量。
  • [&变量名]:按值捕获(能修改)变量,不捕获其他变量。
  • 多个变量名可以逗号分隔。
class CT{
public:int m_i = 5;void myfuncpt(int x, int y){auto mylambda1 = [this] {return m_i;};//不能使用x,yauto mylambda2 = [=] {return m_i;};//能使用x,y,不能修改auto mylambda3 = [&] {return m_i;};//能使用x,y,能修改auto mylambda4 = [this, x, y] {return m_i;};//能使用x,y,不能修改auto mylambda5 = [&x, &y] {return x;};//能使用x,y,能修改}
};

(6)[=, &变量名]:默认按值捕获所有外部变量,其他变量按引用捕获,多个变量逗号隔开。

auto mylambda = [this, &x, y] {return m_i;};
//或者
auto mylambda = [=, &x] {return m_i;};

(7)[&, 变量名]:默认按引用捕获所有外部变量,其他变量按值捕获,多个变量逗号隔开。

auto mylambda = [&, x] {...};

20.8.3 lambda表达式延迟调用易出错细节分析

int x = 5;
auto f = [=] {return x;};//5
x = 10;
cout<

lambda捕获的时刻,已经复制x的值了。
lambda捕获时,已经将所有外部变量值复制一份存储在了lambda表达式变量中。
&引用可以及时访问外部值。

20.8.4 lambda表达式中的mutable

int x = 5;
auto f = [=]() mutable{x = 6; //无mutable时x不能修改return x;
}
x = 10;
cout<
//存在mutable时,即使无参数,()也不能省略
auto f = [=]() mutable{x = 6; //无mutable时x不能修改return x;
}

20.8.5 lambda表达式的类型和存储

lambda表达式的类型称为闭包类型(CLosure Type),闭包:函数内的函数(可调用对象)。
捕获到的外部变量可看作闭包类型的成员变量,这些成员变量在lambda对象创建时被初始化。

auto aa = [](){};
auto bb = [](){};
using boost::typeindex::type_id_with_cvr;
cout<<"aa="<().pretty_name()<().pretty_name()<};//f是一个匿名的类类型对象
std::function fc1 = [](int tv){return tv};
fc1(15);//15//bind中,第一个参数是函数指针
//第二个参数是tv
std::function fc2 = std::bind([](int tv){return tv}, 16);
//bind绑定死tv为16,这里参数15不起作用,除非bind中使用placeholders::_1
//std::bind([](int tv){return tv}, placeholders::_1);
fc2(15);//16

不捕获任何变量的lambda表达式,可转换为普通的函数指针。

using functype = int(*)(int);
functype fp = [](int tv){return tv};
cout<

语法糖是指基于语言现有的特性,构建出一个东西,程序员用起来会很方便。但它没有增加语言的原有功能。lambda表达式可看成定义仿函数闭包(函数中的函数)的语法糖。

20.8.6 lambda表达式再演示和优点总结

  1. for_each中的lambda表达式
    for_each是函数模板,配合函数对象使用,第三个参数就是一个函数对象(可以给进去lambda表达式)。
vector myvector = {10, 20, 30, 40, 50};
int sum = 0;
for_each(myvector.begin(), myvector.end(), [&sum](int val){sum += val;cout<
  1. find_if中的lambda表达式
vector myvector = {10, 20, 30, 40, 50};
auto result = find_if(myvector.begin(), myvector.end(), [](int val){cout<
vector myvector = {10, 20, 30, 40, 50};
auto result = find_if(myvector.begin(), myvector.end(), [](int val){if(val>15)return true;return false;//返回false,find_if会不停遍历myvector,直到返回true
});if(result == myvector.end())cout<<"not find"<

lambda表达式优点:代码简洁、灵活、强大,提高开发效率、可维护性等。

20.9 lambda表达式捕获模式的陷阱分析和展示

20.9.1 捕获列表中的&

引用捕获会导致lambda表达式(闭包)包含绑定到局部变量的引用。

#include 
std::vector> gv;void myfunc(){srand((unsigned)time(NULL));int tmpvalue = rand() % 6;gv.push_back([&](int tv){return tv % tmpvalue == 0;//tmpvalue局部变量,引用悬空});
}myfunc();
cout<

20.9.2 形参列表可以使用auto

c++14允许lambda的形参列表使用auto

	gv.push_back([=](auto tv){return tv % tmpvalue == 0;//tmpvalue会复制一份});

20.9.3 成员变量的捕获问题

class AT{
public:void addItm(){gv.push_back([=](auto tv){//=实际等于thisreturn tv % tmpvalue == 0;//tmpvalue实际是this->tmpvalue, 不会复制一份});}int tmpvalue = 7;
};AT *pat = new AT();
pat->addItem();
delete pat;
cout<
	void addItm(){auto tmpvalueCopy = tmpvalue;gv.push_back([tmpvalueCopy](auto tv){return tv % tmpvalueCopy == 0;//tmpvalueCopy赋值, 不是类AT的成员变量});}

20.9.4 广义lambda捕获

	void addItm(){gv.push_back([tmpvalueCopy = tmpvalue](auto tv){//tmpvalue复制到闭包里来return tv % tmpvalueCopy == 0;});}

20.9.5 静态局部变量

静态局部变量不能被捕获,但能在lambda表达式中使用。

void myfunc(){static int tmpvalue;srand((unsigned)time(NULL));tmpvalue = rand() % 6;gv.push_back([](auto tv){//[],static不需要捕获return tv % tmpvalue == 0;});
}
void myfunc(){static int tmpvalue = 4;gv.push_back([](auto tv){//[],static不需要捕获cout<

20.10 可变参数函数、initializer_list与省略号形参

20.10.1 可变参数函数

initializer_list处理非固定参数,但类型相同。

20.10.2 initializer_list(初始化列表)

参数数量不固定,参数类型相同。
initializer_list中元素是常值,不能被改变。

initializer_list myarray;
initializer_list myarray2 = {12, 14, 16, 20, 30};
  1. begin、end、size
void printvalue(initializer_list tmpstr){for(auto beg = tmpstr.begin(); beg != tmpstr.end(); ++beg)cout<c_str()< tmpstr){for(auto& tmpitem : tmpstr)cout<"aa", "bb", "cc"});void printvalue(initializer_list tmpstr, int tmpvalue){...}
printvalue({"aa", "bb", "cc"}, 6);
  1. 复制和赋值
    复制和赋值initializer_list时,共享列表中的元素。
initializer_list myarray3 = {"aa", "bb", "cc"};
initializer_list myarray4(myarray3);
initializer_list myarray5;
myarray5 = myarray4;
  1. 作为构造函数参数
class CT{
public:CT(initializer_list &tmpvalue){}
};CT ct1 = {10, 220, 30, 40}; //隐式类型转换,explicit可禁止
CT ct2{10, 220, 30, 40};
CT ct3 = CT({10, 220, 30, 40});
  1. 统一初始化
int myarray[] = {1, 5, 6, 9};
int myarray2[]{1, 5, 6, 9};
vector myvec = {1, 5, 6, 9};

编译器看到大括号括起来的形如{“aa”, “bb”, “cc”}的内容,一般会转化为std::initializer_list。

20.10.3 省略号形参(…)

可变参数函数必须至少有一个普通参数。

#include double average(int num, ...){va_list valist;double sum = 0;va_start(valist, num);for(int i=0; i
void funcTest(const char *msg...){int csgs = atoi(msg);va_list valist;va_start(valist, msg);for(int paramcount = 0; paramcount < csgs; paramcount++) {char *p = va_arg(valist, char*);printf("%d: %s\n", paramcount, p);}va_end(valist);
}

注意点:
(1)至少一个有效形参;
(2)省略号形参只能出现在最后的位置;

void myfunc(参数列表, ...);

编译器会对参数列表进行类型检查,函数调用时,省略号对应实参不会进行类型检查。
(3)省略号前逗号可以省略;

void funcTest(const char *msg, ...);
void funcTest(const char *msg...);

(4)多个非可变参时,va_start(valist, msg)绑…前那个形参;

void testFunc(char *pszDest, int DestLen, const char *pszFormat...){}
va_start(valist, pszFormat);

20.11 萃取技术概念与范例等

20.11.1 类型萃取简介

type traits,萃取技术一种可用于编译器根据类型作判断的泛型编程技术。

类型萃取定义了一个编译期间的基于模板的接口,用来查询或者修改类型的属性,可以根据这些类型萃取接口来获取各种信息。
(1)主要类型种类

is_void
is_null_pointer
is_integral
is_floating_point
is_array
is_enum
is_union
is_class
is_function
is_pointer
is_lvalue_reference
is_rvalue_reference
is_member_object_pointer
is_member_function_pointer

(2)复合类型种类

is_fundamental
is_arithmetic
is_scalar
is_object
is_compound
is_reference
is_member_pointer

(3)类型属性

is_const
is_volatile
is_trivial
is_trivially_copyable
is_standard_layout
is_pod
is_literal_type
has_unique_object_representations
is_empty
is_polymorphic
is_abstract
is_final
is_aggregate
is_signed
is_unsigned
is_bounded_array
is_unbounded_array

(4)支持的操作
trivially(平淡的,可有可无的)信息,若构造函数无初始化,就属于trivially。

is_constructible
is_trivially_constructible
is_nothrow_constructibleis_default_constructible
is_trivially_default_constructible
is_nothrow_default_constructibleis_copy_constructible
is_trivially_copy_constructible
is_nothrow_copy_constructibleis_move_constructible
is_trivially_move_constructible
is_nothrow_move_constructibleis_assignable
is_trivially_assignable
is_nothrow_assignableis_copy_assignable
is_trivially_copy_assignable
is_nothrow_copy_assignableis_move_assignable
is_trivially_move_assignable
is_nothrow_move_assignableis_destructible
is_trivially_destructible
is_nothrow_destructiblehas_virtual_destructoris_swappable_with
is_swappable
is_nothrow_swappable_with
is_nothrow_swappable

20.11.2 类型萃取范例

template
void printTraitsInfo(const T& t){cout<<"type name: "<::value<::value<::value<::value<::value<::value<::value<::value<::value<::value<::value<
public:A() = default;A(A&& ta) = delete;A(const A& ta) = delete;virtual ~A(){}
};class B{
public:int m_i;int m_j;
};class C{
public:C(int t){}
};printTraitsInfo(int());
printTraitsInfo(string());
printTraitsInfo(A());
printTraitsInfo(B());
printTraitsInfo(C());
printTraitsInfo(list());

相关内容

热门资讯

【MySQL】锁 锁 文章目录锁全局锁表级锁表锁元数据锁(MDL)意向锁AUTO-INC锁...
【内网安全】 隧道搭建穿透上线... 文章目录内网穿透-Ngrok-入门-上线1、服务端配置:2、客户端连接服务端ÿ...
GCN的几种模型复现笔记 引言 本篇笔记紧接上文,主要是上一篇看写了快2w字,再去接入代码感觉有点...
数据分页展示逻辑 import java.util.Arrays;import java.util.List;impo...
Redis为什么选择单线程?R... 目录专栏导读一、Redis版本迭代二、Redis4.0之前为什么一直采用单线程?三、R...
【已解决】ERROR: Cou... 正确指令: pip install pyyaml
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
Lock 接口解读 前置知识点Synchronized synchronized 是 Java 中的关键字,...
Win7 专业版安装中文包、汉... 参考资料:http://www.metsky.com/archives/350.htm...
3 ROS1通讯编程提高(1) 3 ROS1通讯编程提高3.1 使用VS Code编译ROS13.1.1 VS Code的安装和配置...
大模型未来趋势 大模型是人工智能领域的重要发展趋势之一,未来有着广阔的应用前景和发展空间。以下是大模型未来的趋势和展...
python实战应用讲解-【n... 目录 如何在Python中计算残余的平方和 方法1:使用其Base公式 方法2:使用statsmod...
学习u-boot 需要了解的m... 一、常用函数 1. origin 函数 origin 函数的返回值就是变量来源。使用格式如下...
常用python爬虫库介绍与简... 通用 urllib -网络库(stdlib)。 requests -网络库。 grab – 网络库&...
药品批准文号查询|药融云-中国... 药品批文是国家食品药品监督管理局(NMPA)对药品的审评和批准的证明文件...
【2023-03-22】SRS... 【2023-03-22】SRS推流搭配FFmpeg实现目标检测 说明: 外侧测试使用SRS播放器测...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
初级算法-哈希表 主要记录算法和数据结构学习笔记,新的一年更上一层楼! 初级算法-哈希表...
进程间通信【Linux】 1. 进程间通信 1.1 什么是进程间通信 在 Linux 系统中,进程间通信...
【Docker】P3 Dock... Docker数据卷、宿主机与挂载数据卷的概念及作用挂载宿主机配置数据卷挂载操作示例一个容器挂载多个目...