[杂记]C++中的lambda函数、可变参数模板


这两个内容没有什么联系, 只是放到一起做一下笔记.


1. lambda函数

python中有lambda函数的用法:

list(map(lambda x: x**2, [1, 2, 3, 4]))

输出:

1, 4, 9, 16

lambda函数是对于表达式不太长的较简单的功能, 尽量缩短代码提高可读性的一种手段. 实际上C++里面也有. 如果从例子入手的话, 比如说, 我要对一个类进行自定义排序. 定义如下的牛马类, 对于一个数组的牛马, 将收入从小到大排. 对于收入相同的, 将年龄从大到小排:

class CowHorse{
    
    
private:
    std::string m_name; 
    int m_age;
    double m_salary;
public:
    CowHorse(const std::string& name, const int age, const double salary) : m_name(std::move(name)), m_age(age), m_salary(salary) {
    
    }
    bool operator<(CowHorse& ch);
    friend std::ostream& operator<<(std::ostream& os, CowHorse& ch);
};

方法一. 重载<运算符
因为sort函数默认是通过从小到大排序(std::less), 所以我们重载<运算符:

bool CowHorse::operator<(CowHorse& ch){
    
    
    if (this->m_salary != ch.m_salary)
        return this->m_salary < ch.m_salary;
    else 
        return this->m_age > ch.m_age;
}

运行

void func(){
    
    
    std::vector<CowHorse> arr;
    arr.push_back(CowHorse("amy", 35, 3000));
    arr.push_back(CowHorse("bob", 85, 3000));
    arr.push_back(CowHorse("damengzi", 45, 9000));
    arr.push_back(CowHorse("fool", 25, 1000));

    sort(arr.begin(), arr.end());

    for (auto& item: arr){
    
    
        std::cout << item;
    }

}

输出结果:

fool 25 1000
bob 85 3000
amy 35 3000
damengzi 45 9000

方法二. 仿函数
仿函数, 可以理解为函数对象, 即它可以是一个struct或class, 但用的时候当作函数来用, 为此需要重载()运算符. 定义自定义排序的类如下:

struct myCmp_{
    
    
    bool operator()(CowHorse& ch0, CowHorse& ch1){
    
    
        return ch0 < ch1;
    }
};

调用sort时, 第三个参数传入实例化的myCmp_类:

sort(arr.begin(), arr.end(), myCmp_());

结果也是正确的.
方法三. 函数指针
当然, 更简洁的方式是直接定义函数(因为这个例子比较简单):

bool myCmpFunc(CowHorse& ch0, CowHorse& ch1){
    
    
    return ch0 < ch1;
}

在调用sort时传入函数指针:

sort(arr.begin(), arr.end(), myCmpFunc);

方法四. lambda函数
lambda函数的格式为:

[返回值](参数){
    
    函数体}

或者

[](参数)->返回值类型{
    
    函数体}

因此可以这样写:

sort(arr.begin(), arr.end(), [&](CowHorse& ch0, CowHorse& ch1){
    
    return ch0 < ch1; });

其中[&]在这里用不用这个都可以.

补充: 中括号里的[&][=]的意思是, 按引用或按值的方式捕获表达式前的所有可捕获的变量. 这是什么意思呢? 比如LeetCode的第1030题:

在这里插入图片描述
假如我们用最朴素的直接排序法做的话, 我们需要自己定义排序函数. 在这里, 排序的依据不仅仅是每个点的坐标值, 还与其他变量也就是中心点坐标有关, 所以我们用lambda函数是最方便的. 因此我们排序的时候, 还需要捕获rCentercCenter变量, 就需要用[&][=].这里是int, 所以按值传递就可以了.

class Solution {
    
    
public:
    vector<vector<int>> allCellsDistOrder(int rows, int cols, int rCenter, int cCenter) {
    
    
        vector<vector<int>> ret;
        for (int i = 0; i < rows; i++) {
    
    
            for (int j = 0; j < cols; j++) {
    
    
                ret.push_back({
    
    i, j});
            }
        }
        sort(ret.begin(), ret.end(), [=](vector<int>& a, vector<int>& b) {
    
    
            return abs(a[0] - rCenter) + abs(a[1] - cCenter) < abs(b[0] - rCenter) + abs(b[1] - cCenter);
        });
        return ret;
    }
};

2. 可变参数模板

我们知道, python中有*args与**kwargs用于定义函数的可变参数.

>>> def test(**kwargs):
...     if 'name' in kwargs:
...         print(kwargs['name'])
...
>>> test(name='asds')

因此, 我们在不确定函数输入参数的时候, 可以这样来用, 并且加条件判断即可. 这样有助于泛化性.
C++中也有这项功能, 只不过不如python这么强大(C++中的可变参数不是哈希表, 不能向上面那样索引)

例如, 我们要定义一个函数, 打印它所有的参数值:

void func() {
    
    return;}

template<typename T, typename... Args>  // Args是模板参数包, ...放前面
void func(T t, Args... args){
    
      // 函数参数包作为函数参数
   std::cout << t;  // 打印第一项
   func(args...);  // 递归调用. 函数参数包后面跟省略号. 注意, 当args为空时, 由于func()未定义, 因此要重载一个func的版本.
}



int main(){
    
    
    std::string name = "ajsdqwq";
    func(name, name, name, name);

    system("pause");
    return 0;
}

输出:

ajsdqwqajsdqwqajsdqwqajsdqwq

其中, 模板参数包Args是一个类型的列表, 而函数参数包args是一个值的列表, 二者是一一对应的.

猜你喜欢

转载自blog.csdn.net/wjpwjpwjp0831/article/details/126934392