c++—函数式编程(lambda、函数对象、函数包装器、函数适配器)

1. lambda表达式

    (1)本质:实际是匿名函数,能够捕获一定范围的变量,与普通函数不同,可以在函数内部定义;

    (2)作用:①简化程序结构,因为优化了函数命名与函数传参;②提高程序运行效率,因为优化了函数调用、函数返回等消耗; 适用于简单功能的函数优化;

    (3)捕获总结与示例:

        ① = :按值捕获,只可用不可改

        ② & :按地址捕获,即可用又可改

        ③ 变量名:按值捕获,可用不可改

        ④ &变量名:引用捕获,可用可改

        ⑤ 副本捕获:c++14后可以自定义变量名 = 捕获变量,但是无法通过副本名改变变量名,示例如下:

#include 

using namespace std;

int main(int argc, char **argv)
{
    int num1 = 5;
    int num2 = 6;

    auto func_add = [&num1,num2]()  //num1可修改,num2不可修改
    {
        num1 = 7;
        return num1 + num2;
    };

    auto func_add1 = [=](int a, int b)
    {
        a = 1;  //这里修改的只是形参a/b的值,不会改变num1与num2的值
        b = 1;
        return a + b;
    };

    auto func_add2 = [&]
    {
        // num1 = 1;  //按引用传递,可用可修改num1与num2的值
        return num1 + num2;
    };

    cout<#include 
#include 
#include 

using namespace std;

template 
class Add
{
public:
    Add() = default;
    void operator()(T &&a, T &&b)  //重载函数运算符,采用的是万能引用
    {
        cout< c_add;
    c_add.operator()(5,6);  //利用成员函数的形式调用
    c_add(5,6);    //采用函数成员方式

    plus p1;
    cout<

    (2)函数对象(function object)与普通函数的区别

        ①函数对象比一般函数更灵活,因为它可以拥有状态(state),事实上,对于相同的函数对象可以设置两个状态不同的实例;普通函数没有状态;

        ②每个函数对象都有其类型,因为你可以将函数对象的类型当做template参数传递,从而指定某种行为;另外容器类型也会因为函数对象的不同而不同;

        ③执行速度上,函数对象通常比函数指针更快;

3. 函数包装器

    (1)本质与作用

        ①统一所有的函数调用形式,全部面向对象

        ②本质是一个类模板,用于包装可调用对象,可以容纳除了类成员之外(函数)指针之外的所有可调用形式;

        ③头文件:#include; 包装器程序示例:

#include 
#include 
#include 

using namespace std;

template 
class Add
{
public:
    Add() = default;
    void operator()(T &&a, T &&b)
    {
        cout< c_add;

    auto f_add = [](int a, int b)  //lambda表达式
    {
        return a + b;
    };

    //函数包装器,统一所有的函数调用形式,全部面向对象;
    functionfunc1(add);  //对普通函数包装
    cout<<"add : "<func2(f_add);  //对lambda表达式包装
    cout<<"lambda : "<func3(c_add);  //对函数对象包装
    cout<<"functor : ";
    func3(5,6);

    return 0;
}

4. bind 函数适配器

    (1)主要用在函数已经存在,但是现有参数较多,减少实际所需参数个数的一种方法;

    (2)本质,bind也是一个函数模板,返回值是一个仿函数,是可调用对象;

    (3)bind可以绑定的对象:①普通函数;②lambda表达式;③函数对象;④类的成员函数;⑤类的数据成员;

示例:

#include 
#include 
using namespace std;

template 
class Add
{
public:
    T operator()(T a, T b, T c)
    {
        print();
        return a + b;
    }

    void operator()(const T &a)
    {
        cout << a << endl;
    }

    void print()
    {
        cout << "function add!" << endl;
    }

    int m_result;
};

int add(int a, int b, int c)
{
    cout << "a = " << a << " b = " << b << endl;
    return a + b + c;
}

int main()
{
    //普通函数
    function my_add = std::bind(add,std::placeholders::_1,std::placeholders::_2,0);
    cout << my_add(5,6) << endl;
    function my_add2 = std::bind(add,7,8,0);
    cout << my_add2() << endl;

    //lambda表达式
    auto lambda_func = [=](int a, int b, int c)
    {
        return a + b + c;
    };
    function my_add3 = std::bind(lambda_func,std::placeholders::_2,std::placeholders::_1,0);
    cout << my_add3(3,4) << endl;
    function my_add4 = std::bind(lambda_func,3,4,0);
    cout << my_add4() << endl;

    //函数对象
    Add c_add;
    function my_add5 = std::bind(c_add,std::placeholders::_2,std::placeholders::_1,0);
    cout << my_add5(4,5) << endl;
    function my_add6 = std::bind(c_add,5,6,0);
    cout << my_add6() << endl;

    return 0;
}

5. 关键字constexpr

    (1)引入的原因:因为const不能确保其修饰的变量、函数能够在编译器优化,从而引入constexpr确保被其修饰的变量或者函数能够在编译器优化;从c++11开始引入;

    (2)主要的优点:让函数执行提前到编译期,减少函数调用返回的消耗;

    (3)注意点

        ①修饰变量时:变量必须是常量、变量必须立即被初始化、变量的类型不可以是类类型;

        ②修饰表达式时:表达式的值不会改变且在编译期就可求值的表达式;

        ③修饰指针时:仅对指针有效,与指向的对象无关,即指针的指向不可发生变化;

        ④constexpr修饰的函数中不可以出现循环体;且函数必须不能是虚函数(基类中被virtual修饰的成员函数);函数体中不可以出现try和goto语句;constex只可以调用其他conste函数,不可以调用非conste函数;

你可能感兴趣的:(c++语言学习,linux,c语言,c++,开发语言)