C++ 函数对象、函数指针与Lambda表达式
函数指针
函数指针(Function Pointer)是指向函数的指针变量。它可以存储函数的地址,并通过该指针变量来调用该函数。函数指针的声明使用指针符号 ,指向的类型为函数的返回类型和参数列表,如 int (funcPtr)(int, int);。函数指针的值可以指向相同返回类型和参数列表的函数。
类型名(*函数名)(参数列表)
int add(int a, int b) {return a + b;
}int main() {int(*fun)(int, int) = add;cout << fun(1,2) << endl;
}
函数对象
在C++中,函数对象(Function Objects)是一种类或结构体,它重载了函数调用运算符operator()
,因此可以像函数一样被调用。函数对象有时也被称为仿函数(Functor)。
- 使用函数对象:函数对象可以像普通函数一样被调用,通过在对象后加括号并传递参数来执行操作。例如:
- 重载operator():函数对象需要重载operator(),并根据需要定义参数和返回值。通过重载operator(),函数对象就可以像函数一样被调用。
- 状态保持:与普通函数不同的是,函数对象可以包含状态。这意味着函数对象可以在其内部保持一些状态信息,并在每次调用时进行更新。这使得函数对象更加灵活且功能强大。
- 模板函数对象:函数对象可以是模板类,可以接受不同类型的参数。这样可以实现更通用和灵活的函数对象,适用于多种情况。
- 标准库中的函数对象:C++标准库提供了许多预定义的函数对象,如std::plus、std::minus、std::greater等,可以直接使用这些函数对象完成特定的操作,而不用自己定义函数对象。
- 使用场景:函数对象通常用于泛型编程、STL算法、排序、自定义比较函数等情况。通过函数对象,我们可以定义自己的函数行为,并将其应用于各种数据结构和算法中。
class Max
{
public:
/*
first_argument_type: 表示第一个参数的类型。
second_argument_type: 表示第二个参数的类型。
result_type: 表示返回值的类型。
*/typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return x>y?x:y;}
};// 将函数对象作为参数传递给函数
void testMaxNum(int a, int b, Max m) {cout << m(a, b) << endl;
}int main() {Max max1;cout << max1(5,6) << endl;// test functionalcout << testMaxNum(5,6, Max());
}
functional
function 模板的参数就是函数的类型,一个函数对象放到 function 里之后,外界可以观察到的就只剩下它的参数、返回值类型和执行效果了。注意 function 对象的创建还是比较耗资源的,所以请你只在用 auto 等方法解决不了问题的时候使用这个模板。
function<返回值(参数列表)>
function<int(int,int)> add = [](int x, int y) {return x + y;};
谓词
返回bool值得函数对象,一般用于排序等操作
常见二元谓词:
- great_equal
: 大于等于 - great
: 大于 - less
: 小于 - less_equal
小于等于
// 可以自己实现谓词
class Cmp
{
public:bool operator()(int x, int y) {return x < y;}
};void display(vector<int>& vec) {for(auto v : vec) {cout << v << " ";}cout << endl;
}int main() {vector<int> vec({1,2,3,2,4,5,6});cout << "=== 二元谓词 === " << endl;sort(vec.begin(), vec.end(), greater<int>());display(vec);sort(vec.begin(), vec.end(),less<int>());display(vec);sort(vec.begin(), vec.end(),greater_equal<int>());display(vec);sort(vec.begin(), vec.end(), less_equal<int>());display(vec);
}
绑定
bind1st and bind2nd
C++98函数,C++17已被bind函数替代,适应于函数对象,可以绑定指定值到函数对象的第一/二个参数
案例:
void test1() {auto fun1 = std::bind1st(plus<int>(), 5);auto fun2 = std::bind2nd(plus<int>(), 4);cout << "bind the first args: " << fun1(4) << endl;cout << "bind the second args: " << fun2(5) << endl;
}
bind模板
可以绑定任意参数数量的函数,配合占位符(std::placeholders::_x,x代表参数几)使用
案例:
void test2() {auto fun1 = std::bind(plus<int>(), std::placeholders::_1, 5);auto fun2 = std::bind(plus<int>(), 4, std::placeholders::_1);cout << "bind the first args: " << fun1(4) << endl;cout << "bind the second args: " << fun2(5) << endl;
}
测试代码:
#include <iostream>
#include <functional>
using namespace std;class Max
{
public:
/*
first_argument_type: 表示第一个参数的类型。
second_argument_type: 表示第二个参数的类型。
result_type: 表示返回值的类型。
*/typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return x>y?x:y;}
};class RightNum
{
public:typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return y;}
};
//int add(int x, int y) {return x + y;
} int main() {// functional objectcout << "Max(x, y) = ";Max m_max;cout << m_max(10,11) << endl;// bind1st and bind2ndcout << "===test bind1st and bind2nd===\n";auto rightFun1 = bind1st(RightNum(), 0);auto rightFun2 = bind2nd(RightNum(), 0);cout << "(0, y)->right num = ";cout << rightFun1(10) << endl;cout << "(x, 0)->right num = ";cout << rightFun2(10) << endl;// test bind// bind(fun, args...)cout << "=========test bind===============\n";cout << "functional object\n";auto rightNum3 = bind(RightNum(),std::placeholders::_1, 1);auto rightNum4 = bind(RightNum(),1, std::placeholders::_1);cout << rightNum3(10) << endl;cout << rightNum4(10) << endl;cout << "functional pointer\n";cout << "add(1, y) = ";auto adder = bind(add, 1, std::placeholders::_1);cout << adder(10) << endl;// labmdacout << "===test labmda===" << endl;int(*fun_l1)(int,int) = [](int x, int y) {return x + y;};cout << fun_l1(1,2) << endl;
}
lambda表达式
lambda
表达式是C++11
中引入的一项新技术,利用lambda
表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读。
[捕获列表] (形参列表) mutable 异常列表-> 返回类型
{函数体
}
-
捕获列表:捕获外部变量,捕获的变量可以在函数体中使用(可省略,即不捕获外部变量)
[]:默认不捕获任何变量;
[=]:默认以值捕获所有变量;
[&]:默认以引用捕获所有变量;
[x]:仅以值捕获x,其它变量不捕获;
[&x]:仅以引用捕获x,其它 变量不捕获;
[=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
[&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
[this]:通过引用捕获当前对象(其实是复制指针);
[*this]:通过传值方式捕获当前对象; -
形参列表:和普通函数的形参列表一样(可省略,即无参数列表)
-
mutable:mutable 关键字,如果有,则表示在函数体中可以修改捕获变量
-
异常列表:noexcept / throw(...),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常。
-
返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型。
-
函数体:代码实现。可省略,但是没意义。
void testLambda() {auto f = []() {return 1;}auto f2 = [=](int x, int y) {return a+b;}
}
Lambda表达式的底层实现
Lambda的本质是函数对象(仿函数),即Lambda对象调用operator()函数。
其实现类似如下:
class Lambda_xxx
{public:Lambda_xxx(int a1, int a2, args...):args1(a1), args2(a2){};return_type operator(int a1, int a2,args...) const {/*函数体*/}
private:int args1;int args2;/*....*/
};
其中,类名 lambda_xxxx 的 xxxx 是为了防止命名冲突加上的。
- lambda 表达式中的捕获列表,对应 lambda_xxxx 类的 private 成员
- lambda 表达式中的形参列表,对应 lambda_xxxx 类成员函数 operator() 的形参列表
- lambda 表达式中的 mutable,表明 lambda_xxxx 类成员函数 operator() 的是否具有常属性 const,即是否是 常成员函数
- lambda 表达式中的返回类型,对应 lambda_xxxx 类成员函数 operator() 的返回类型
- lambda 表达式中的函数体,对应 lambda_xxxx 类成员函数 operator() 的函数体
另外,lambda 表达 捕获列表的捕获方式,也影响 对应 lambda_xxxx 类的 private 成员 的类型
值捕获:private 成员的类型与捕获变量的类型一致
引用捕获:private 成员 的类型是捕获变量的引用类型
class task
{
public:task(int a, int b):m_a(a), m_b(b){}void sum() {auto res = [this]() {return this->m_a + this->m_b;}return res();}
private:int m_a;int m_b;
};//返回Lambda表达式
auto add = [](int n) {return [n](int x) {return n + x;}
}cout << add(1)(2) << endl;
Lambda函数对象与函数指针的转换
如果 lambda 表达式不捕获任何外部变量,则存在Lambda函数对象到函数指针的转换.
lambda_xxxx => 函数指针
int(*p)(int,int) = [](int a, int b) {return a + b;
}
参考:C++ 学习之函数对象_c++函数对象-CSDN博客