C++ 函数对象、函数指针与Lambda表达式

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)。

  1. 使用函数对象:函数对象可以像普通函数一样被调用,通过在对象后加括号并传递参数来执行操作。例如:
  2. 重载operator():函数对象需要重载operator(),并根据需要定义参数和返回值。通过重载operator(),函数对象就可以像函数一样被调用。
  3. 状态保持:与普通函数不同的是,函数对象可以包含状态。这意味着函数对象可以在其内部保持一些状态信息,并在每次调用时进行更新。这使得函数对象更加灵活且功能强大。
  4. 模板函数对象:函数对象可以是模板类,可以接受不同类型的参数。这样可以实现更通用和灵活的函数对象,适用于多种情况。
  5. 标准库中的函数对象:C++标准库提供了许多预定义的函数对象,如std::plus、std::minus、std::greater等,可以直接使用这些函数对象完成特定的操作,而不用自己定义函数对象。
  6. 使用场景:函数对象通常用于泛型编程、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 functional
    cout << testMaxNum(5,6, Max());
}

functional

function 模板的参数就是函数的类型,一个函数对象放到 function 里之后,外界可以观察到的就只剩下它的参数、返回值类型和执行效果了。注意 function 对象的创建还是比较耗资源的,所以请你只在用 auto 等方法解决不了问题的时候使用这个模板。

function<返回值(参数列表)>
function<int(int,int)> add = [](int x, int y) {
        return x + y;
    };

谓词

返回bool值得函数对象,一般用于排序等操作

常见二元谓词:

  1. great_equal : 大于等于
  2. great : 大于
  3. less : 小于
  4. 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 object
    cout << "Max(x, y) = ";
    Max m_max;
    cout << m_max(10,11) << endl;
    // bind1st and bind2nd
    cout << "===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;

    // labmda
    cout << "===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 是为了防止命名冲突加上的。

  1. lambda 表达式中的捕获列表,对应 lambda_xxxx 类的 private 成员
  2. lambda 表达式中的形参列表,对应 lambda_xxxx 类成员函数 operator() 的形参列表
  3. lambda 表达式中的 mutable,表明 lambda_xxxx 类成员函数 operator() 的是否具有常属性 const,即是否是 常成员函数
  4. lambda 表达式中的返回类型,对应 lambda_xxxx 类成员函数 operator() 的返回类型
  5. 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博客

posted @ 2024-11-08 19:28  RunTimeErrors  阅读(2)  评论(0编辑  收藏  举报