• modern C++:闭包与匿名函数


    最近放假在写一个小项目,用到了闭包和匿名函数的知识,记录一下

    What?

    匿名函数:匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中。C++从C++11开始支持。
    闭包:闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持函数编程的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)

    匿名函数只是一个没有名字的函数,闭包是一个函数指针+配套环境,闭包是一个函数的实例。
    当匿名函数内也有外界定义的变量时就变成了闭包,当闭包没有外界变量时可以优化成一个普通函数(不一定是匿名函数)

    why?

    闭包的存在是为了保护私有变量不被污染,形成不销毁的栈内存,里面的私有变量等信息保存下来。
    匿名函数常用于回调函数、事件处理器、或者在需要临时定义函数的地方。它们有助于减少代码的复杂性,使代码更加简洁。

    how?

    匿名函数是一种技术,只有lambda表达式一种实现。
    闭包是一种思想,可以用不同方式来实现

    方法一:重载()操作符

    C++允许进行操作符重载,可以将一个类的()操作符重载,就可以使这个类的实例可以调用,Eg:

    #include 
    
    class Operator {
        public:
            int operator ()(int a, int b) {
                return a+b+bias;
            }
        private:
            int bias = 1;
    };
    
    int main() {
        Operator op;
        std::cout << op(1, 2) << std::endl;
    
        return 0;
    }
    

    好奇,能不能将这个重载声明为static,试了一下发现报错说运算符不能是静态成员函数。

    方法二:lambda表达式

    C++11中引入了lambda表达式,lambda是函数式编程中的概念,用于定义匿名函数,Eg:

    #include 
    #include 
    
    int main() {
        int bias = 1;
        std::function<int(int)> f = [bias](int x) { return x + bias; };
        // lambda函数,bias是捕获的变量,x是参数,
        // 如果不捕获变量,可以写成[=],如果捕获所有变量,可以写成[&]
        // 不捕获变量时,就是一个普通的函数,捕获后就变成了闭包
        std::cout << f(1) << std::endl;
        return 0;
    }
    

    方法三:参数绑定

    C++11在标准库中新增了bind函数,std::bind是C++中实现函数参数绑定的一种方式,它允许你创建一个可调用的函数对象,该对象可以存储一部分参数,并在需要时再提供剩余的参数。这在某些情况下非常有用,比如在多线程编程中,你可能需要将参数和函数传递给不同的线程。

    C++11之前boost中有参数绑定函数bind,可以调用boost::bind函数

    使用bind定义闭包如下:

    #include 
    #include 
    
    int add(int a, int b) {
        std::cout << "a: " << a << " b: " << b << std::endl;
        return a + b;
    }
    
    int main()
    {
        auto f = std::bind(add, std::placeholders::_2, std::placeholders::_1);
        // placeholders可以理解为占位符,_1表示第一个参数,_2表示第二个参数,以此类推
        // 上面的代码表示将add函数的第二个参数作为f的第一个参数,第一个参数作为f的第二个参数
        // 所以调用f(1,2)时,实际上调用的是add(2,1)
        std::cout << f(1,2) << std::endl;
    
        auto f2 = std::bind(add, 1, std::placeholders::_1);
        // 上面的代码表示将add函数的第一个参数固定为1,第一个参数作为f2的第二个参数
        std::cout << f2(2) << std::endl;
    
        auto f3 = std::bind(add, std::placeholders::_1, 2);
        // 上面的代码表示将add函数的第二个参数固定为2,第一个参数作为f3的第一个参数
        std::cout << f3(1) << std::endl;
    
        auto f4 = std::bind(add, 1, std::placeholders::_2);
        // 上面的代码表示将add函数的第一个参数固定为1,第二个参数作为f4的第二个参数
        // 所以调用时必须给两个参数
        std::cout << f4(1,2) << std::endl;
        return 0;
    }
    

    使用时要注意C++不会因为闭包而延长变量的生命周期,在lambda中引用一个已经释放了的变量是一种未定义行为,文档原话:
    If a non-reference entity is captured by reference, implicitly or explicitly, and operator() of the closure object is invoked after the entity’s lifetime has ended, undefined behavior occurs. The C++ closures do not extend the lifetimes of objects captured by reference.

    others

    • C语言
      C语言中没有语法可以支持闭包,但是C语言支持回调函数,可以通过回调函数来达到类似于闭包的效果。
      在C语言中,支持回调函数的库有时在注册时需要两个参数:一个函数指针,一个独立的void*指针用以保存用户数据。这样的做法允许回调函数恢复其调用时的状态。这样的惯用法在功能上类似于闭包,但语法上有所不同。
    • Python
      Python从一开始就支持闭包,在Python中经常用闭包来实现装饰器
    import functools
    import time
    
    def logger(func):
        """装饰器函数,用于记录函数的调用信息"""
        @functools.wraps(func)  # 保持原始函数的名称和文档字符串
        def wrapper(*args, **kwargs):
            print(f"Calling {func.__name__} at {time.ctime()}")
            result = func(*args, **kwargs)
            print(f"{func.__name__} returned {result}")
            return result
        return wrapper
    
    # 使用装饰器
    @logger
    def add(a, b):
        """Add two numbers."""
        return a + b
    
    # 调用被装饰的函数
    result = add(3, 4)
    
  • 相关阅读:
    浏览器检测麦克风音量
    2022-2028全球平板电脑主板行业调研及趋势分析报告
    注意力机制是否比矩阵分解更好?——IS ATTENTION BETTER THAN MATRIX DECOMPOSITION?
    AI大语言模型时代构建全新数据平台
    SpringMVC快速入门(2022优化版)
    建筑能源管理(5)——建筑能源审计和审计方法
    MongoDB副本集特点验证
    RobotFramework框架之导入自己打包的python程序(十五)
    mongodb 数据分析
    windows查看并关闭端口对应进程占用的命令
  • 原文地址:https://blog.csdn.net/qq_45983373/article/details/140359541