c++函数指针 lambda 表达式 std::function std::bind
c/c++ 函数指针的用法
【目录】
c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);
c++函数指针的定义形式:返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….);
以下代码编译环境:codeblocks with gcc in win 7
#include <stdio.h>
#include <stdlib.h>
int fun1()
{
printf("this is fun1 call\n");
return 1;
}
void fun2(int k, char c)
{
printf("this is fun2 call:%d %c\n", k, c);
}
int main()
{
int (*pfun1)() = NULL;
void (*pfun2)(int, char) = NULL;
int a,b;
pfun1 = fun1; //第一种赋值方法
a = pfun1(); //第一种调用方法(推荐)
printf("%d\n",a);
b = (*pfun1)();//第二种调用方法
printf("%d\n",b);
pfun2 = &fun2;//第二种赋值方法(推荐,因为和其他数据指针赋值方法一致)
pfun2(1,'a');
(*pfun2)(2,'b');
return 0;
}
#include <iostream>
using namespace std;
class test
{
public:
test()
{
cout<<"constructor"<<endl;
}
int fun1(int a, char c)
{
cout<<"this is fun1 call:"<<a<<" "<<c<<endl;
return a;
}
void fun2(double d)const
{
cout<<"this is fun2 call:"<<d<<endl;
}
static double fun3(char buf[])
{
cout<<"this is fun3 call:"<<buf<<endl;
return 3.14;
}
};
int main()
{
// 类的静态成员函数指针和c的指针的用法相同
double (*pstatic)(char buf[]) = NULL;//不需要加类名
pstatic = test::fun3; //可以不加取地址符号
pstatic("myclaa");
pstatic = &test::fun3;
(*pstatic)("xyz");
//普通成员函数
int (test::*pfun)(int, char) = NULL; //一定要加类名
pfun = &test::fun1; //一定要加取地址符号
test mytest;
(mytest.*pfun)(1, 'a'); //调用是一定要加类的对象名和*符号
//const 函数(基本普通成员函数相同)
void (test::*pconst)(double)const = NULL; //一定要加const
pconst = &test::fun2;
test mytest2;
(mytest2.*pconst)(3.33);
// //构造函数或者析构函数的指针,貌似不可以,不知道c++标准有没有规定不能有指向这两者的函数指针
// (test::*pcon)() = NULL;
// pcon = &test.test;
// test mytest3;
// (mytest3.*pcon)();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
void fun(int k, char c)
{
printf("this is fun2 call:%d %c\n", k, c);
}
void fun1(void (*pfun)(int, char), int a, char c)
{
pfun(a, c);
}
int main()
{
fun1(fun, 1, 'a');
return 0;
}
// c++ 的形式差不多
// c 形式
#include <stdio.h>
#include <stdlib.h>
void fun(int k, char c)
{
printf("this is fun2 call:%d %c\n", k, c);
}
//fun1 函数的参数为double,返回值为函数指针void(*)(int, char)
void (*fun1(double d))(int, char)
{
printf("%f\n",d);
return fun;
}
int main()
{
void (*p)(int, char) = fun1(3.33);
p(1, 'a');
return 0;
}
//c++ 形式
#include <iostream>
using namespace std;
class test
{
public:
int fun(int a, char c)
{
cout<<"this is fun call:"<<a<<" "<<c<<endl;
return a;
}
};
class test2
{
public:
// test2 的成员函数fun1,参数是double,
//返回值是test的成员函数指针int(test::*)(int, char)
int (test::*fun1(double d))(int, char)
{
cout<<d<<endl;
return &test::fun;
}
};
int main()
{
test mytest;
test2 mytest2;
int (test::*p)(int, char) = mytest2.fun1(3.33);
(mytest.*p)(1, 'a');
return 0;
}
#include <stdio.h>
#include <stdlib.h>
float add(float a,float b){return a+b;}
float minu(float a,float b){return a-b;}
int main()
{
//定义一个函数指针数组,大小为2
//里面存放float (*)(float, float)类型的指针
float (*pfunArry[2])(float, float) = {&add, &minu};
double k = pfunArry[0](3.33,2.22);// 调用
printf("%f\n", k);
k = pfunArry[1](3.33,2.22);
printf("%f\n", k);
return 0;
}
//c++ 可类比
#include <stdio.h>
#include <stdlib.h>
float add(float a,float b)
{
printf("%f\n",a+b);
return a+b;
}
float minu(float a,float b)
{
printf("%f\n",a-b);
return a-b;
}
//用pfunType 来表示float(*)(float, float)
typedef float(*pfunType)(float, float);
int main()
{
pfunType p = &add;//定义函数指针变量
p(3.33, 2.22);
pfunType parry[2] = {&add, &minu};//定义函数指针数组
parry[1](3.33, 2.22);
//函数指针作为参数可以定义为:void fun(pfunType p)
//函数指针作为返回值可以定义为:pfunType fun();
return 0;
}
//c++ 可类比
【版权声明】转载请注明出处 http://www.cnblogs.com/TenosDoIt/p/3164081.html
来自:https://www.cnblogs.com/TenosDoIt/p/3164081.html
=========
c++函数指针作为参数:
在C++中,可以通过将函数指针作为参数来实现更高阶的函数,即那些接收另一个函数作为参数的函数。这种技术在需要回调函数或策略模式等场景中非常有用。下面是一个简明的示例,展示了如何定义和使用函数指针作为参数。
示例1:简单的函数指针
假设我们有两个函数,它们都符合某种签名(即参数和返回类型一致),然后我们定义一个接受这些函数指针作为参数的函数:
在这个示例中,我们定义了一个函数指针类型Operation
,它可以指向任何符合int(int, int)
签名的函数。然后,我们定义了两个这样的函数add
和multiply
。在executeOperation
函数中,我们使用函数指针来调用传递进来的具体函数。
示例2:使用std::function
和std::bind
C++11引入了std::function
和std::bind
,使得处理函数对象(包括函数指针、lambda表达式和其他可调用对象)更加方便和灵活。
在这个示例中,我们使用std::function
来接受任何符合签名int(int, int)
的可调用对象,包括函数指针、lambda表达式和通过std::bind
生成的函数对象。这样,代码的灵活性和可读性得到了提升。
总结
通过使用函数指针或者std::function
,C++程序可以实现高阶函数,从而实现更灵活和可扩展的设计。使用std::function
和std::bind
提供了更多的功能和更好的类型安全性,特别是在C++11及其后的版本中。
====================
前面讲完了lambda
表达式的基本使用,最后给出lambda
表达式的完整语法:
// 完整语法
[ capture-list ] ( params ) mutable(optional) constexpr(optional)(c++17) exception attribute -> ret { body }
// 可选的简化语法
[ capture-list ] ( params ) -> ret { body }
[ capture-list ] ( params ) { body }
[ capture-list ] { body }
第一个是完整的语法,后面3个是可选的语法。这意味着lambda
表达式相当灵活,但是照样有一定的限制,比如你使用了拖尾返回类型,那么就不能省略参数列表,尽管其可能是空的。针对完整的语法,我们对各个部分做一个说明:
- capture-list:捕捉列表,这个不用多说,前面已经讲过,记住它不能省略;
- params:参数列表,可以省略(但是后面必须紧跟函数体);
- mutable:可选,将
lambda
表达式标记为mutable
后,函数体就可以修改传值方式捕获的变量; - constexpr:可选,C++17,可以指定
lambda
表达式是一个常量函数; - exception:可选,指定
lambda
表达式可以抛出的异常; - attribute:可选,指定
lambda
表达式的特性; - ret:可选,返回值类型;
- body:函数执行体。
作者:小白将
链接:https://www.jianshu.com/p/d686ad9de817
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
C++11的一大亮点就是引入了Lambda表达式。利用Lambda表达式,可以方便的定义和创建匿名函数。对于C++这门语言来说来说,“Lambda表达式”或“匿名函数”这些概念听起来好像很深奥,但很多高级语言在很早以前就已经提供了Lambda表达式的功能,如C#,Python等。今天,我们就来简单介绍一下C++中Lambda表达式的简单使用。
声明Lambda表达式
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:
序号 | 格式 |
---|---|
1 | [capture list] (params list) -> return type {function body} |
2 | [capture list] (params list) {function body} |
3 | [capture list] {function body} |
其中:
- 格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
- 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型: (1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定; (2):如果function body中没有return语句,则返回值为void类型。
-
格式3中省略了参数列表,类似普通函数中的无参函数。
讲了这么多,我们还没有看到Lambda表达式的庐山真面目,下面我们就举一个实例。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool cmp(int a, int b)
{
return a < b;
}
int main()
{
vector<int> myvec{ 3, 2, 5, 7, 3, 2 };
vector<int> lbvec(myvec);
sort(myvec.begin(), myvec.end(), cmp); // 旧式做法
cout << "predicate function:" << endl;
for (int it : myvec)
cout << it << ' ';
cout << endl;
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); // Lambda表达式
cout << "lambda expression:" << endl;
for (int it : lbvec)
cout << it << ' ';
}
在C++11之前,我们使用STL的sort函数,需要提供一个谓词函数。如果使用C++11的Lambda表达式,我们只需要传入一个匿名函数即可,方便简洁,而且代码的可读性也比旧式的做法好多了。
下面,我们就重点介绍一下Lambda表达式各项的具体用法。
捕获外部变量
Lambda表达式可以使用其可见范围内的外部变量,但必须明确声明(明确声明哪些外部变量可以被该Lambda表达式使用)。那么,在哪里指定这些外部变量呢?Lambda表达式通过在最前面的方括号[]来明确指明其内部可以访问的外部变量,这一过程也称过Lambda表达式“捕获”了外部变量。
我们通过一个例子来直观地说明一下:
#include <iostream>
using namespace std;
int main()
{
int a = 123;
auto f = [a] { cout << a << endl; };
f(); // 输出:123
//或通过“函数体”后面的‘()’传入参数
auto x = [](int a){cout << a << endl;}(123);
}
上面这个例子先声明了一个整型变量a,然后再创建Lambda表达式,该表达式“捕获”了a变量,这样在Lambda表达式函数体中就可以获得该变量的值。
类似参数传递方式(值传递、引入传递、指针传递),在Lambda表达式中,外部变量的捕获方式也有值捕获、引用捕获、隐式捕获。
1、值捕获
值捕获和参数传递中的值传递类似,被捕获的变量的值在Lambda表达式创建时通过值拷贝的方式传入,因此随后对该变量的修改不会影响影响Lambda表达式中的值。
示例如下:
int main()
{
int a = 123;
auto f = [a] { cout << a << endl; };
a = 321;
f(); // 输出:123
}
这里需要注意的是,如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值。
2、引用捕获
使用引用捕获一个外部变量,只需要在捕获列表变量前面加上一个引用说明符&。如下:
int main()
{
int a = 123;
auto f = [&a] { cout << a << endl; };
a = 321;
f(); // 输出:321
}
从示例中可以看出,引用捕获的变量使用的实际上就是该引用所绑定的对象。
3、隐式捕获
上面的值捕获和引用捕获都需要我们在捕获列表中显示列出Lambda表达式中使用的外部变量。除此之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。
隐式值捕获示例:
int main()
{
int a = 123;
auto f = [=] { cout << a << endl; }; // 值捕获
f(); // 输出:123
}
隐式引用捕获示例:
int main()
{
int a = 123;
auto f = [&] { cout << a << endl; }; // 引用捕获
a = 321;
f(); // 输出:321
}
4、混合方式
上面的例子,要么是值捕获,要么是引用捕获,Lambda表达式还支持混合的方式捕获外部变量,这种方式主要是以上几种捕获方式的组合使用。
到这里,我们来总结一下:C++11中的Lambda表达式捕获外部变量主要有以下形式:
捕获形式 | 说明 |
---|---|
[] | 不捕获任何外部变量 |
[变量名, …] | 默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符) |
[this] | 以值的形式捕获this指针 |
[=] | 以值的形式捕获所有外部变量 |
[&] | 以引用形式捕获所有外部变量 |
[=, &x] | 变量x以引用形式捕获,其余变量以传值形式捕获 |
[&, x] | 变量x以值的形式捕获,其余变量以引用形式捕获 |
修改捕获变量
前面我们提到过,在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。那么有没有办法可以修改值捕获的外部变量呢?这是就需要使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量,示例:
int main()
{
int a = 123;
auto f = [a]()mutable { cout << ++a; }; // 不会报错
cout << a << endl; // 输出:123
f(); // 输出:124
}
Lambda表达式的参数
Lambda表达式的参数和普通函数的参数类似,那么这里为什么还要拿出来说一下呢?原因是在Lambda表达式中传递参数还有一些限制,主要有以下几点:
- 参数列表中不能有默认参数
- 不支持可变参数
- 所有参数必须有参数名
常用举例:
{
int m = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5); std::cout << "m:" << m << std::endl; //输出m:16 std::cout << "n:" << [](int x, int y) { return x + y; }(5, 4) << std::endl; //输出n:9 auto gFunc = [](int x) -> function<int(int)> { return [=](int y) { return x + y; }; }; auto lFunc = gFunc(4); std::cout << lFunc(5) << std::endl; auto hFunc = [](const function<int(int)>& f, int z) { return f(z) + 1; }; auto a = hFunc(gFunc(7), 8); int a = 111, b = 222; auto func = [=, &b]()mutable { a = 22; b = 333; std::cout << "a:" << a << " b:" << b << std::endl; }; func(); std::cout << "a:" << a << " b:" << b << std::endl; a = 333; auto func2 = [=, &a] { a = 444; std::cout << "a:" << a << " b:" << b << std::endl; }; func2(); auto func3 = [](int x) ->function<int(int)> { return [=](int y) { return x + y; }; };
std::function<void(int x)> f_display_42 = [](int x) { print_num(x); }; f_display_42(44);
}
来自:https://www.cnblogs.com/DswCnblog/p/5629165.html
============
====================
参考:
https://www.cnblogs.com/TenosDoIt/p/3164081.html
chatgpt
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端