首页 > 编程语言 >C++ 函数对象、函数指针与Lambda表达式

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

时间:2024-11-08 19:18:52浏览次数:5  
标签:函数 int 捕获 C++ 对象 函数指针 lambda type 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博客

标签:函数,int,捕获,C++,对象,函数指针,lambda,type,Lambda
From: https://www.cnblogs.com/runtimeerror/p/18535761

相关文章

  • 08C++选择结构(2)——教学
    一、逻辑变量教学视频存储类似灯亮或灯灭、是男还是女等结果只有两种可能的数据时,可以使用逻辑型变量。逻辑型变量用关键字bool定义,所以又称为布尔变量,其值只有两个false(假)和true(真),false和true是逻辑常量,又称布尔常量。流程图如下:英汉小词典:bool:布尔false:假true:真二、逻......
  • C++ 可变参数模板递归展开
    #include<iostream>usingnamespacestd;template<typenameHead,typename...Tail>doubleMax(Headfirst,Tail...rest){doubleMaxnum=0;Maxnum=Max(rest...);if(Maxnum<first)Maxnum=first;returnMaxnum;}......
  • 【QT项目】QT6项目之基于C++的通讯录管理系统(联系人/学生管理系统)
    目录一.项目背景二.创建工程工程创建添加文件 联系人类 功能类三.功能实现联系人类person.cppperson.h 查查询按钮槽函数返回按钮槽函数findperson.cpp:增addperson.cpp:删deleteperson.cpp:改changeperson.cpp:!!显示!!displayperson.cpp:清除clearp......
  • C++ 模板实参推断和引用折叠
    两个例外规则假定i是一个int对象,我们可能认为像£3(i)这样的调用是不合法的。毕竟,i是一个左值,而通常我们不能将一个右值引用绑定到一个左值上。但是,C++语言在正常绑定规则之外定义了两个例外规则,允许这种绑定。这两个例外规则是move这种标准库设施正确工作的基础。第一个例......
  • 07C++选择结构(1)
    一、基础知识1、关系运算符因为我们要对条件进行判断,必然会用到关系运算符:名称大于大于等于小于小于等于等于不等于符号>>=<<===!=关系表达式的值是一个逻辑值,即“真”(True)或“假”(False)。如果条件成立,其值为“真”;如果条件不成立,其值为“假”。2、逻......
  • 【C++进阶】智能指针的使用及原理(1)
    1.智能指针的使用场景分析下面程序中我们可以看到,new了以后,我们也delete了,但是因为抛异常导,后面的delete没有得到执行,所以就内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后delete内存,再把异常抛出,但是因为new本身也可能抛异常,连续的两个new和下面的Divide都可能会抛异......
  • c++执行shell操作
     message("----->>>>>shelldemo/CMakeLists.txt")#收集当前目录下所有的.cpp文件aux_source_directory(.DIR_SRCS)#添加可执行文件add_executable(shelldemo${DIR_SRCS})#链接需要的库target_link_libraries(shelldemo${LIB_LIB})/shelldemo/CMakeList......
  • 【C++篇】无序中的法则:探索 STL之unordered_map 与 unordered_set容器的哈希美学
    文章目录C++`unordered_map`和`unordered_set`容器详解前言第一章:`unordered_map`和`unordered_set`的概念1.1`unordered_map`和`unordered_set`的定义1.2与`map`、`set`的区别第二章:`unordered_map`和`unordered_set`的构造方法2.1`unordered_map`......
  • Chromium 进程降权和提权模拟示例c++
     一、背景知识概念参考微软链接:强制完整性控制-Win32应用程序|Microsoft学习授权)(模拟级别-Win32apps|MicrosoftLearnDuplicateTokenEx函数(securitybaseapi.h)-Win32apps|MicrosoftLearn本文主要演示 low,medium,high,andsystem四种权限创建......
  • C++之OpenCV入门到提高004:Mat 对象的使用
    一、介绍今天是这个系列《C++之Opencv入门到提高》得第四篇文章。这篇文章很简单,介绍如何使用Mat对象来实例化图像实例,了解它的构造函数和常用的方法,这是基础,为以后的学习做好铺垫。虽然操作很简单,但是背后有很多东西需要我们深究,才能做到知其然知其所以然。OpenCV具......