现代 C++ 小利器:参数绑定包装器堪称「Lambda 小平替」

以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」mp.weixin.qq.com/s/gt_zxMwhu...

C++ 原生支持函数输入参数的默认值,但是有些业务场景下对原有设定的默认值不满意,那么可不可以临时改改?

请注意,随意改变原有代码逻辑,并不符合软件设计原则。有没有什么方案或者特性可以做到不修改原有代码逻辑,以实现新增参数默认值(或者在调用时绑定特性参数)的功能?

从 C++11 开始,标准库提供了 std::bind 用于绑定函数 f 和调用参数,返回一个新可调用函数对象 fn (也有喊它做仿函数的)。于是,在调用函数对象 fn 和输入新参数时,相当于调用了原来的函数 f 并自动按照绑定的关系映射参数,映射的方式非常灵活。这样被绑定映射的参数,实现原有函数 f 的参数新默认值功能,或者减少调用函数 f 时输入的参数个数等便捷需求。

简而言之,std::bind 被称为转发调用包装器。

绑定时,通过占位符 _1, _2, _3... ,指定在调用函数对象 fn 的输入参数和传递给函数 f 的参数之间的映射位置。

被绑定的函数 f 也分种类,那么,基于不同类型函数的应用示例是怎样的呢?

绑定静态或者全局函数

cpp 复制代码
#include <functional>

void output1(int a)
{
    printf("%d\n", a);
}

void output2(int a, int b)
{
    printf("%d %d\n", a, b);
}

int main(int argc, char * argv[])
{
    auto fn1 = std::bind(output1, std::placeholders::_1);
    fn1(1);
    auto fn2 = std::bind(output2, std::placeholders::_2, 0);
    fn2(3, 2);
    return 0;
}

output:

1
2 0

占位符代表函数对象的输入参数,std::placeholders::_1 对应调用函数对象 fn 时的第 1 个输入参数,依次类推。

细心的你可能会留意 std::bind 返回的是可调用对象,这个和同样从 C++11 开始引入的 Lambda 表达式概念是类似的,那么有没有可能用 Lambda 表达式互相替换?试试改为使用 Lambda 表达式实现

cpp 复制代码
int main(int argc, char * argv[])
{
    auto fn1 = [](int a) -> void {
        output1(a);
    };
    fn1(1);
    auto fn2 = [](int a, int b) -> void {
        output2(b, 0);
    };
    fn2(3, 2);
    return 0;
}

这样子替换后,执行输出结果是一样的。

output:

1
2 0

绑定对象成员函数

如果被绑定的函数是对象成员,需要指定所属对象,看代码

cpp 复制代码
#include <functional>

class A
{
public:
    void output(int a, int b)
    {
        printf("%d %d\n", a, b);
    }
};

int main(int argc, char * argv[])
{
    A obj;
    auto fn = std::bind(&A::output, &obj, std::placeholders::_2, std::placeholders::_1);
    fn(5, 3);
    return 0;
}

output:

3 5

用 Lambda 表达式替换试试

cpp 复制代码
int main(int argc, char * argv[])
{
    A obj;
    auto fn = [&obj](int a, int b) -> void {
        obj.output(b, a);
    };
    fn(5, 3);
    return 0;
}

output:

3 5

Notice:绑定的时候需要指定上下文环境(实例对象),这种场景已经涉及到闭包的概念了。由于上下文环境是有生命周期的,比如上面用到的实例对象 a,如果在超出了其生命周期的情况下,引用其内部已被释放的资源,是会引起程序执行异常的,这里提醒一下。

平替 Lambda

以上面两种场景示例代码的比较,在参数绑定的场景下,为了达到相同效果,Lambda 表达式的书写略显罗嗦,而 std::bind 的格式更有函数式编程的味道,可见,某些场景下推荐 std::bind 替代 Lambda 表达式。

能不能平替也分场景,三思而行。

相关推荐
熬夜学编程的小王12 分钟前
C++类与对象深度解析(一):从抽象到实践的全面入门指南
c++·git·算法
CV工程师小林14 分钟前
【算法】DFS 系列之 穷举/暴搜/深搜/回溯/剪枝(下篇)
数据结构·c++·算法·leetcode·深度优先·剪枝
2402_8575893622 分钟前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊31 分钟前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso34 分钟前
Rust 快速入门(一)
开发语言·后端·rust
sco528234 分钟前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
zh路西法41 分钟前
基于opencv-C++dnn模块推理的yolov5 onnx模型
c++·图像处理·pytorch·opencv·yolo·dnn·yolov5
原机小子1 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码1 小时前
详解JVM类加载机制
后端
-指短琴长-1 小时前
BFS解决多源最短路问题_01矩阵_C++【含多源最短路问题介绍+dist数组介绍】
c++·矩阵·宽度优先