1. lambda 表达式各部分说明
- [capture-list]:捕捉列表,该列表总是出现在 lambda 函数的开始位置,编译器根据 [] 来判断接下来的代码是否为 lambda 函数,捕捉列表能够捕捉上下文中的变量供 lambda 函数使用。
- (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同 () 一起省略。
- mutable:默认情况下,lambda 函数总是一个 const 函数,mutable 可以取消其常量性。使用该修饰符时,参数列表不可省略 (即使参数为空)。
- ->return-type:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
- {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
2. 捕获列表说明
捕获列表描述了上下文中那些数据可以被 lambda 使用,以及使用的方式传值还是传引用。
- [var]:表示值传递方式捕捉变量 var。
- [=]:表示值传递方式捕获所有父作用域中的变量 (包括 this)。
- [&var]:表示引用传递捕捉变量 var。
- [&]:表示引用传递捕捉所有父作用域中的变量 (包括 this)。
注意:
a. 父作用域指包含 lambda 函数的语句块。
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量 a 和 b,值传递方式捕捉其他所有变量;[&, a, this]:值传递方式捕捉变量 a 和 this,引用方式捕捉其他变量。
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:= 已经以值传递方式捕捉了所有变量,捕捉 a 重复。
d. 在块作用域以外的 lambda 函数捕捉列表必须为空。
f. lambda 表达式之间不能相互赋值,即使看起来类型相同。
代码演示:
void test_lambda()
{
vector<Goods> G{ {"apple",1},{"banana",2} };
/*sort(G.begin(), G.end(),comUp());*/
sort(G.begin(), G.end(), [](const Goods& g1, const Goods& g2)->bool {return g1.price > g2.price; });
for (auto e : G)
{
cout << e._name << ":" << e.price << endl;
}
//lambda运用!可以把它看成一个对象!它的底层其实还是一个仿函数!
auto f1 = [](int x, int y) {int ret = x + y; cout << ret << endl; return 0; };
f1(1, 2);
auto f2 = [](int x)
{
cout << x << endl;
return 0;
};
f2(0);
//捕获列表的使用!
// []就是这个括号里面可以随意调用同一个作用域下的,任意的变量,但是要记住,它调用的时候[&x,&y],加了引用这个属于引用捕捉
// 此时x和y是默认为const类型,当然加了引用就不加mutable
//想要解决这个const就加一个mutable
int x = 1; int y = 0;
auto f0 = [&x,&y]() mutable {int ret = x + y; cout << ret << endl; return 0; };
//捕获全局块作用域
auto ff = [=]() {cout << x << endl; };
//捕获后单独修改个
auto fff = [=, &y]() {cout << x << endl; y++; cout << y << endl; };
//放在作成员函数的时候可以捕捉整个父类作用域!
2.包装器
1. std::function
std::function
是 C++ 标准库提供的一个通用多态函数包装器,它可以存储、复制和调用任何可调用对象,比如普通函数、成员函数、lambda 表达式、函数对象等。
void swap_1(int& x, int& y)
{
cout << "swap" << endl;
}
void test_functional()
{
包装器-
int x = 1; int y = 0;
auto f1 = [&x, &y]() mutable {int ret = x + y; cout << ret << endl; return 0; };
comDown dw();
function<void(int&, int&)> f_1 = swap_1;
f_1(x,y);
function<bool(const Goods&, const Goods&)> fff = dw();
/* fff(x, y);*/
/* Goods g1(10);
Goods g2(20);*/
}
2. std::bind
std::bind
用于将可调用对象与其参数进行绑定,生成一个新的可调用对象。它可以预先绑定部分参数,也能调整参数的顺序。
void test_111()
{
int res = sum(1, 2, 3, 4, 5);
cout << res << endl;
}
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
static int plusi(int a, int b)//静态成员没有this指针!
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
//function属于类模版 而bind属于函数模版!
int main()
{
/*ShowList(1);
ShowList(1, 'A');
ShowList(1, 'A', std::string("sort"));*/
/* test_lambda();*/
// 函数名(函数指针)
//std::function<int(int, int)> func1 = f;
//cout << func1(1, 2) << endl;
函数对象
//std::function<int(int, int)> func2 = Functor();
//cout << func2(1, 2) << endl;
lamber表达式
//std::function<int(int, int)> func3 = [](const int a, const int b) {return a + b; };
//cout << func3(1, 2) << endl;
类的成员函数//记住普通函数名就是地址所以看见普通函数没有加引用,但是成员函数是名并不是地址,所以需要加引用符号和类域!
//std::function<int(int, int)> func4 = &Plus::plusi;
//cout << func4(1, 2) << endl;
//std::function<double(Plus, double, double)> func5 = &Plus::plusd;
//cout << func5(Plus(), 1.1, 2.2) << endl;
Plus()匿名对象属于右值,右值不能取地址,所以不能加引用!
//bind应用-----------------
//调整参数个数,有些参数可能就被写死了,
/*auto fit = bind(f, 5, placeholders::_1);
int ret=fit(2);
cout << ret << endl;*/
test_111();
return 0;
}
3. 智能指针作为包装器
智能指针(如 std::unique_ptr
、std::shared_ptr
和 std::weak_ptr
)也是一种包装器,它们用于管理动态分配的内存,能够自动释放内存,避免内存泄漏。(后续再补充)
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
void doSomething() { std::cout << "Doing something..." << std::endl; }
};
int main() {
// 使用 std::unique_ptr 包装 MyClass 对象
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
ptr->doSomething();
return 0;
}
3.万能模版:
这个万能模版万能在了不管你传入的参数是右值还是左值都可以使用。但是要注意的是,一定要注意在调用的时候是嵌套的而且嵌套的时候需要使用forward<>()它是可以帮你自动匹配你是左值还是右值!
template<class T>
void Print(T&& t)
{
cout << t << endl;
}
//模版里面的万能引用:
template<class T>
void PerfectForward(T&& t)//在这个参数这里,你给它传左右值都可以!
{
//这里的forward叫做完美转发,它可以让你传入的参数,也可以自动配对!
Print(forward<T>(t));//叫做完美转换!
}
4.总结
首先说一下,lambda表达式,对于这个表达式让我们可以更加方便的使用,减少对一些函数的编写,完全可以代替仿函数和一些简单函数,但是一定要熟练使用的格式,在这里再提一下decltype,它的使用是可以带到参数里面的!切记,你用auto 出来的其实可以理解为一个新的函数,所以当你cout或者干什么的时候,是需要用函数那样用的 :cout<<f();
其次就是bind,使用它的时候要注意一些参数的编写,第一个不能少了函数地址,其次就是参数是可以单个固定,未固定的参数需要用placeholders用占位符标记,你用几个占位符,你调用刚刚封装的新的函数时候就要输入几个参数!
然后是function<返回类型(参数)> 新函数名=旧函数。注意它可以使用的对象都有哪些!