C++ 基础与深度分析 Chapter10 泛型算法(bind、lambada表达式、泛型算法的改进ranges)

概述

在这里插入图片描述
很多算法允许通过可调用对象自定义计算逻辑的细节

transform
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如何定义可调用对象:

  • 函数指针:概念直观,但定义位置受限
    在这里插入图片描述
    c++不支持在函数内部定义函数,所以位置是受限的。

  • 类:功能强大,但书写麻烦
    通过操作符重载,为类定义可调用对象。

  • bind :基于已有的逻辑灵活适配,但描述复杂逻辑时语法可能会比较复杂难懂

  • lambda 表达式:小巧灵活,功能强大

bind

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;

bool MyPredict2(int val1, int val2)
{
    return val1 > val2;
}
int main()
{
    using namespace std::placeholders;
    std::vector<int> x{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::vector<int> y{};
    std::copy_if(x.begin(), x.end(), std::back_insert_iterator(y), std::bind(MyPredict2, _1, 3));
    for (auto p : y)
    {
        cout << p << ' '; // 4-10
        cout << endl;
    }
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;

bool MyPredict2(int val1, int val2)
{
    return val1 > val2;
}
int main()
{
    using namespace std::placeholders;
    auto x = std::bind(MyPredict2, _1, 3); // _1代表调用x时,接收x的第1个参数
    x(50); // 50会作为MyPredict2的第1个参数
    cout << x(50) << endl; // 1 true
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;

bool MyPredict2(int val1, int val2)
{
    return val1 > val2;
}

bool MyAnd(bool val1, bool val2)
{
    return val1 && val2;
}
int main()
{
    using namespace std::placeholders;
    auto x1 = std::bind(MyPredict2, _1, 3); 
    auto x2 = std::bind(MyPredict2, 10, _1); 
    auto x3 = std::bind(MyAnd, x1, x2);
    cout << x3(5) << endl; // 1 意思为:3 < 5 < 10
}

调用 std::bind 时,传入的参数会被复制,这可能会产生一些调用风险
用智能指针解决
在这里插入图片描述
可以使用 std::ref 或 std::cref 避免复制的行为
在这里插入图片描述
bind还有一个缺点,就是复杂逻辑很难看懂代码。

std::bind_front ( C++20 引入): std::bind 的简化形式
绑定第一个参数。
在这里插入图片描述
在这里插入图片描述

lambada表达式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
参数与函数体:

int main()
{
    auto x = [](int val) { return val > 3; }; // (函数形参) {函数体,多条语句,要用分号隔离}
    cout << x((5)) << endl; // 1, 5 > 3
}
int main()
{
    auto x = [](int val) { return (val > 3) && (val < 10); }; 
    cout << x((5)) << endl; // 1
}

在这里插入图片描述
返回类型的自动推导c++11。如果引入更多的return语句,所有的return语句的类型必须是相同的,这是前提。

int main()
{
    auto x = [](int val) 
    {
        if (val > 3)
        {
            return 3.0;
        }
        else
        {
            return 1.5;
        }
    
    }; 
    cout << x((5)) << endl; // 3,自动推导x的类型是double
}
int main()
{
    // 告诉编译器返回类型
    auto x = [](int val) -> float 
    {
        if (val > 3)
        {
            return 3.0;
        }
        else
        {
            return 1.5f;
        }
    
    }; 
    cout << x((5)) << endl; // 3
}

捕获: 针对函数体中使用的局部自动对象进行捕获

int main()
{
    int y = 10;

    auto x = [](int val)
    {
        return val > y; // 报错,在lambada表达式内部,不知道y是什么
    }; 
    cout << x((5)) << endl; 
}
int main()
{
    int y = 10; // 局部自动对象

    auto x = [y](int val)
    {
        return val > y; // 把y放到[]内进行捕获
    }; 
    cout << x((5)) << endl; 
}
int main()
{
    static int y = 10; // y是一个局部静态对象,不需要捕获,直接使用

    auto x = [](int val)
    {
        return val > y; 
    }; 
    cout << x((5)) << endl; 
}

值捕获

int main()
{
    int y = 10;

    auto x = [y] (int val) mutable
    {
        ++y;
        return val > y; 
    }; 
    cout << x((5)) << endl; 
    cout << y << endl; // 10 lambada对y的修改,不会传递到外部,因为是值捕获,y是复制到lambada内部
}

引用捕获

int main()
{
    int y = 10;

    auto x = [&y] (int val)
    {
        ++y;
        return val > y; 
    }; 
    cout << x((5)) << endl; 
    cout << y << endl; // 11 y是引用捕获
}

混合捕获

int main()
{
    int y = 10;
    int z = 3;

    // 对y进行引用捕获,对z进行值捕获 --> 混合捕获
    auto x = [&y, z] (int val) mutable 
    {
        ++y;
        return val > z; 
    }; 
    cout << x((5)) << endl; // 1
    cout << y << endl; // 11
}

如果lambada[捕获列表]里要捕获的值太多,这样就比价麻烦。大部分对象如果都是值捕获的,我们在[]里写个=。所有在{}使用的局部自动对象都值捕获了。我们也可以在[]中写入&,表示在{}使用的局部自动对象都引用捕获了。

int main()
{
    int y = 10;
    int z = 3;

    // []除了z是值捕获,其他都是引用捕获
    auto x = [&, z] (int val) mutable 
    {
        ++y;
        return val > z; 
    }; 
    cout << x((5)) << endl; 
    cout << y << endl; 
}

this 捕获

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;

struct Str{
    auto fun()
    {
        int val = 3; // 局部自动对象
        // this 表示如果构造了Str对象,this就是一个指针,指向Str对象的地址
        auto lam = [val, this] ()  
        {
            return val > x;
        }; 
        return lam();
    }
    int x;
};

int main()
{
    Str s;
    s.fun();
}

初始化捕获( C++14 )

int main()
{
    int x = 3;
    // 初始化捕获,构造对象y,将x赋予y
    auto lam = [y = x](int val)
    {
        return val > y;
    };
    cout << lam(100) << endl;
}

在这里插入图片描述

int main()
{
    int x = 3;
    int y = 10;
    // z是在编译期计算出来的
    auto lam = [z = x + y](int val)
    {
        return val > z;
    };
    cout << lam(100) << endl;
}

*this 捕获( C++17 )
在这里插入图片描述
说明符
mutable

int main()
{
    int y = 3;
    auto lam = [y](int val) mutable
    {
        ++y; // 不能通过编译,除非加上mutable
        return val > y;
    };
}

constexpr (C++17)

int main()
{
    int y = 3;
    auto lam = [y](int val) constexpr
    {
        return val + 1; // 表示这个lam可以在编译期进行调用
    };
    constexpr int val = lam(100);
    cout << val << endl;
}

consteval (C++20)

int main()
{
    int y = 3;
    auto lam = [y](int val) consteval
    {
        return val + 1; // consteval只能在编译期调用
    };
    constexpr int val = lam(100);
    cout << val << endl;
}

lambada表达式的深入应用

在这里插入图片描述
捕获时计算

int main()
{
    int x = 3;
    int y = 5;
    // 捕获时计算
    auto lam = [z = x + y]()
    {
        return z;
    };
    lam();
}

即调用函数表达式( Immediately-Invoked Function Expression, IIFE )

int main()
{
    int x = 3;
    int y = 5;
    // 捕获时计算
    const auto val = [z = x + y]()
    {
        return z;
    }(); // 即调用函数表达式( Immediately-Invoked Function Expression, IIFE )
    // 构造完lam,构造完,马上执行lam 表达式
    // 执行完的值,马上赋给val,不需要额外定义函数
}

使用 auto 避免复制( C++14 )

int main()
{
    auto lam = [](auto x)
    {
        return x + 1;
    };
}
int main()
{
    std::map<int, int> m{{2, 3}};
    // 虽然用了引用,但是还是复制了
    auto lam = [](const std::pair<int, int>& p)
    {
        return p.first + p.second;
    };
    cout << lam(*m.begin()) << endl; // 5
}
int main()
{
    std::map<int, int> m{{2, 3}};
    auto lam = [](const auto& p)
    {
        return p.first + p.second;
    };
    cout << lam(*m.begin()) << endl; // 5
}

Lifting ( C++14 )

#include <iostream>
#include <map>
using namespace std;

auto fun(int val)
{
    return val + 1;
}

auto fun(double val)
{
    return val + 1;
}

int main()
{
    // auto x,来决定用哪个fun
    auto lam = [](auto x)
    {
        return fun(x);
    };
    cout << lam(3) << endl; // 4
    cout << lam(3.5) << endl; // 4.5
    // auto b = std::bind(fun, 3); // bind不知道调用哪个fun了
    // cout << b() << endl;
}

递归调用( C++14 )

#include <iostream>
#include <map>
using namespace std;

/*
int factorial(int n)
{
    return n > 1 ? n * factorial(n - 1) : 1;
}*/

int main()
{
    // 这么写递归调用的lambada表达式会报错
    auto factorial = [](int n){
        return n > 1 ? n * factorial(n - 1) : 1;
    };
    cout << factorial(5) << endl; // 120
}

泛型算法的改进ranges

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <vector>
#include <ranges>
using namespace std;


int main()
{
    std::vector<int> x{1, 2, 3, 4, 5};
   //  auto it = std::ranges::find(x.begin(), x.end(), 3);
    auto it = std::ranges::find(x, 3);
    cout << *it << endl;
}

在这里插入图片描述
view并不是对输入的东西立即计算,而是需要的时候计算,把提升程序的性能。view模糊了容器和算法的概念了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值