STL的泛型算法使用详解(2)

一、CPP之lamba表达式

1、实践体会lamba表达式

(1)lambda表达式使用举例:

if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; }))
{
	std::cout << "All numbers are even\n";
}

lamba表达式:[](int i){ return i % 2 == 0; }

(2)使用函数对象实现类似于lambda表达式的效果

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

struct IsEven//若使用class关键字,则需将重载函数作为public成员,class和struct
{			 //关键字默认权限不一样
    bool operator()(int i)
    {
        return i % 2 == 0;
    }
};

int main(int argc, char **)
{
    vector<int> v(10, 3);

    //if (all_of(v.begin(), v.end(), [](int i) ->bool {return i % 2 == 0;}))//方式一:使用lamba表达式
    //IsEven()不传参,这个()对应使用构造函数进行初始化的参数,i是内部传参,迭代器遍历时容器中的元素
    if (all_of(v.begin(), v.end(), IsEven()))//方式二:使用函数对象
    {
        std::cout << "All numbers are even\n";
    }
	else
	{
		std::cout << " Not all numbers are  even\n";
	}

    return 0;
}

2、lambda表达式总结

(1)lambda表达式 就是一个匿名函数,相当于一次使用的、直接原地展开调用的函数

(2)lambda表达式也叫闭包,就是在别的地方无法调用的封包

(3)lambda表达式其实就是一个函数对象,在内部创建了一个重载()操作符的类

3、lambda表达式格式

(1)完整格式5部分:[参数捕获] (操作符重载函数参数) mutable或exception声明 ->返回值类型 {函数体}

(2)最简单的lambda表达式:[](){},调用执行时为[](){}();

(3)带传参的lambda表达式:[](int i){//i在这里可以用}

(4)使用auto将lambda表达式定义为一个变量,再以变量方式调用

(5)使用->int这种方式让lambda表达式函数返回相应类型的值
示例如下:
	(2) [](){}();
	
	(3) [](){cout << "hello" << endl;}();
		[](int i){cout << "hello" << i << endl;}(5);

	(4)(5) auto func = [](int i) ->bool {cout << "hello" << i << endl; return false;};
	bool x = func(4);
	cout << "x = " << x << endl;

二、lambda表达式之参数捕获

1、什么是参数捕获

(1)实验:在lambda表达式外面定义int a,在表达式内部试图访问

(2)解决:在[]中增加捕获说明即可

#include <iostream>
using namespace std;

class test
{
private:
    int sum;
public:
    test(){};
    test(int t){this->sum = t;};
    void func(void)
    {
        cout << "sum = " << sum <<endl;
        [&](){sum++;cout<<"lamba4: sum = " << sum <<endl;}();
        [this](){this->sum++;cout<<"lamba5: this->sum = " << this->sum << endl;}();
    }

};

int main(int argc, char *argv[])
{
    int a = 5;
    string b = "test";
    [a](){cout << "lamba1: a = " << a << endl;}();
    [&a](){a++; cout << "lamba2: a = " << a << endl;}();
    auto func = [=](int i){cout << "lamba3: i = " << i << " a = " << a << " b = " << b << endl;};
    func(4);

    test s(10);
    s.func();

    return 0;
}

(3)总结:所谓参数捕获,就是让lambda表达式内部可以捕获并使用外部的变量

2、lambda表达式的捕获列表

(1)[] 空,完全不捕获

(2)[=] 等号,以值传参方式捕获(复制了一份传进来,这样表达式内修改了变量,但表达式外值
仍未变,与引用传参的效果不同),捕获范围是表达式所在作用范围(包括所在类的this(3)[&] &号,以引用传参方式捕获,捕获范围是表达式所在作用范围(包括所在类的this(4)[this] 只捕获lambda表达式所在类的this可访问的那些

(5)[a] 仅以值方式捕获a,其他全部不捕获

(6)[&a] 仅以引用方式捕获a,其他全部不捕获

(7)[a, &b] 仅以值方式捕获a,以引用方式捕获b,其余完全不捕获

(8)[=, &a, &b] 仅以引用方式捕获a和b,其余以值方式捕获

(9)[&, a, b] 仅以值方式捕获a和b,其余以引用方式捕获

3、lambda表达式总结

(1)lambda表达式提供一种单次使用的函数简写方式

(2)通过捕获列表,在lambda表达式内部也可以访问外部的变量,相当于函数传参

三、CPP函数适配器

1、什么是函数适配器

(1)适配器,adapter,用来在不适配的2端间对接的连接器

(2)函数适配器是在不同传参个数的函数间进行适配的技术

示例理解:
func1(int a)
func2(int a, char b)//可通过手动填充一个参数b进行适配
func3(int a){func2(a, 'A');}//func3则是一个函数适配器,适配了func1与func2

(3)几个概念:1元函数、2元函数、1元谓词、2元谓词
1元:具有一个参数
2元:具有两个参数
谓词:返回值是bool类型

2、C++的函数适配器

(1)早期C++98时,常用bind1st bind2nd
bind1st:预先填充第一个,释放第二个,bind2nd与其刚好相反
(2)C++11开始,引入加强版:std::bind

if (std::none_of(v.cbegin(), v.cend(), std::bind(std::modulus<int>(),                                                 std::placeholders::_1, 2)))
//std::placeholders::_1:表示释放谁,2:表示要填充的值,即将二元谓词的第一个元释放出来,第二个元预先填充,形成一个新的一元谓词
{
	std::cout << "None of them are odd\n";
}

template< class InputIt, class UnaryPredicate >
bool all_of( InputIt first, InputIt last, UnaryPredicate p );//UnaryPredicate表示一元谓词

std::modulus::operator()//二元谓词
T operator()( const T& lhs, const T& rhs ) const;
(C++14)
constexpr T operator()( const T& lhs, const T& rhs ) const;
(C++14)
返回 lhs 除以 rhs 的余数。

一元谓词和二元谓词并不适配,故需要函数适配器bind

3、bind的学习

参考学习:https://blog.csdn.net/u013654125/article/details/100140328

#include <iostream>
#include <functional>
using namespace std;
 
int TestFunc(int a, char c, float f)
{
    cout << a << endl;
    cout << c << endl;
    cout << f << endl;
 
    return a;
}
 
int main()
{
    auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1);
    bindFunc1(10);
 
    cout << "=================================\n";
 
    auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
    bindFunc2('B', 10);//char c ,int a,即B对应c,10对应a 
    
 
    cout << "=================================\n";
 
    auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
    bindFunc3(100.1, 30, 'C');
 
    return 0;
}

四、STL典型泛型算法解读

1、for_each、transform、范围 for 循环(C++11)

参考学习:
https://zh.cppreference.com/w/cpp/algorithm/for_each
https://zh.cppreference.com/w/cpp/algorithm/transform
https://zh.cppreference.com/w/cpp/language/range-for

在这里插入图片描述

struct Sum
{
    void operator()(int n) { sum += n; }
    int sum{0};//使用{}赋初值,若struct Sum类型变量被定义,sum初值为0
};
for_each示例:

可能源码:
template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
    for (; first != last; ++first) {
        f(*first);
    }
    return f; // C++11 起隐式移动
}
使用示例:
std::vector<int> nums{3, 4, 2, 8, 15, 267};
auto print = [](const int& n) { std::cout << " " << n; };
 
std::cout << "before:";
std::for_each(nums.cbegin(), nums.cend(), print);//实现遍历元素并打印
std::cout << '\n';
transform示例:
可能源码:

版本一
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first, 
                   UnaryOperation unary_op)
{
    while (first1 != last1) {
        *d_first++ = unary_op(*first1++);
    }
    return d_first;
}

使用示例:
std::string s("hello");
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) -> unsigned char { return std::toupper(c); });
范围 for 循环示例:

可能源码:
属性(可选) for ( 初始化语句(可选)范围变量声明 : 范围表达式 ) 循环语句		

使用示例:
#include <iostream>
#include <vector>
 
int main() {
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
 
    for (const int& i : v) // 以 const 引用访问
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto i : v) // 以值访问,i 的类型是 int
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto&& i : v) // 以转发引用访问,i 的类型是 int&
        std::cout << i << ' ';
    std::cout << '\n';
 
    const auto& cv = v;
 
    for (auto&& i : cv) // 以转发引用访问,i 的类型是 const int&
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (int n : {0, 1, 2, 3, 4, 5}) // 初始化器可以是花括号初始化器列表
        std::cout << n << ' ';
    std::cout << '\n';
 
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // 初始化器可以是数组
        std::cout << n << ' ';
    std::cout << '\n';
 
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // 不必使用循环变量
    std::cout << '\n';
 
    for (auto n = v.size(); auto i : v) // 初始化语句(C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
 
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // typedef 声明作为初始化语句(C++20)
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // 别名声明作为初始化语句,同上(C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

2、再看几个泛型算法

https://zh.cppreference.com/w/cpp/algorithm
其他算法的学习方法与上边相同,打开上边的网址自主学习,授之以🐟不如授之以渔。
在这里插入图片描述

2、泛型算法总结

(1)理解 谓词 和 函数对象,是学习和使用泛型算法的基础

(2)泛型算法很多,但是套路是类似的,学会的关键是理解并会用这种套路

(3)不建议试图死记硬背所有泛型算法,建议实战中去熟悉、去用起来并记住

(4)能用现成泛型算法写代码就不要自己造轮子

(5)一个潜在问题警告:C++包的越来越多(层层封装),不要想当然,未经验证确认的代码都要有怀疑精神

注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值