IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ std::function/bind/cref/mean_fn/lambda -> 正文阅读

[C++知识库]C++ std::function/bind/cref/mean_fn/lambda

std::function

  • std::function是函数模板类(是一个类),定义在头文件 functional

  • 类模板 std::function 是一个通用的多态函数包装器。std::function 的实例可以存储、复制和调用任何 可复制构造、可调用的目标函数、lambda 表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。

  • 存储的可调用对象称为 std::function 的目标。 如果 std::function 不包含目标,则称为空。 调用空 std::function 的目标会导致抛出 std::bad_function_call异常。

  • 成员函数:

    • operator= 或者assign 分配一个新目标
    • operator bool 检测是否包含目标
    • operator () 调用目标
  • 注意:

    • std::function 的结果类型是从没有尾随返回类型的 lambda表达式初始化的引用时,应小心。 由于自动推导的工作方式,这样的 lambda表达式将始终返回一个纯右值。 因此,结果引用通常会绑定到一个临时对象,该临时对象的生命周期在 std::function::operator() 返回时结束。

      std::function<const int&()> F([]{ return 42; });
      int x = F(); // 未定义的行为:F() 的结果是一个悬空引用
      
  • 案例:

    #include <functional>
    #include <iostream>
    
    struct Foo {
       Foo(int num) : num_(num) {}
       void print_add(int i) const { std::cout << num_+i << '\n'; }
       int num_;
    };
    
    void print_num(int i)
    {
       std::cout << i << '\n';
    }
    
    struct PrintNum {
       void operator()(int i) const
       {
           std::cout << i << '\n';
       }
    };
    
    int main()
    {
       // store a free function 存储一个自由函数
       std::function<void(int)> f_display = print_num;
       f_display(-9);  // -9
    
       // store a lambda  存储一个lambda
       std::function<void()> f_display_42 = []() { print_num(42); };
       f_display_42();  // 42
    
       // store the result of a call to std::bind  存储调用 std::bind 的结果
       std::function<void()> f_display_31337 = std::bind(print_num, 31337);
       f_display_31337(); // 31337
    
       // store a call to a member function  存储对成员函数的调用
       std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
       const Foo foo(314159);	
       f_add_display(foo, 1);  // 314160
       f_add_display(314159, 1);  // 314160
    
       // store a call to a data member accessor 存储对数据成员访问器的调用
       std::function<int(Foo const&)> f_num = &Foo::num_;
       std::cout << "num_: " << f_num(foo) << '\n' // num_: 314159 上式引入了num的值
    
       // store a call to a member function and object 存储对成员函数和对象的调用
       using std::placeholders::_1;
       std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
       f_add_display2(2); // 314161  =314159+2
    
       // store a call to a member function and object ptr 存储对成员函数和对象 ptr 的调用
       std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
       f_add_display3(3);  // 314162  =314159+3
    
       // store a call to a function object  存储对函数对象的调用
       std::function<void(int)> f_display_obj = PrintNum();
       f_display_obj(18);   == 18
    
       auto factorial = [](int n) {
           // store a lambda object to emulate "recursive lambda"; aware of extra overhead   存储一个 lambda 对象以模拟“递归 lambda”;意识到额外的开销
           std::function<int(int)> fac = [&](int n){ return (n < 2) ? 1 : n*fac(n-1); };
           // note that "auto fac = [&](int n){...};" does not work in recursive calls
           return fac(n);
       };
       // 5! 6! 7!
       for (int i{5}; i != 8; ++i) { std::cout << i << "! = " << factorial(i) << ";  "; }
    }
    

std::ref/cref

解释:

  • std::ref 用于包装按引用传递的值。
  • std::cref 用于包装按const引用传递的值。

作用:

  • 函数模板refcref是辅助函数,它们生成std::reference_wrapper类型的对象,使用模板参数推导来确定结果的模板参数。
  • bind()是一个函数模板,它的原理是根据已有的模板,生成一个函数,但是由于bind()不知道生成的函数执行的时候,传递进来的参数是否还有效。所以它选择参数值传递而不是引用传递。如果想引用传递,std::refstd::cref就派上用场了。

案例:

// f 函数定义
 void f(int n1, int n2, int n3, const int& n4, int n5) {
     std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
 }
 
 void main() {
 	int n = 7;
	// (_1 and _2 are from std::placeholders, and represent future
	// arguments that will be passed to f1)
	auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
	n = 10;
	f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
	               // makes a call to f(2, 42, 1, n, 7)
 } 

output:
	2 42 1 10 7

std::mem_fn

  • 函数模板 std::mem_fn 为指向成员的指针生成包装对象,它可以存储、复制和调用指向成员的指针。 调用 std::mem_fn 时可以使用对对象的引用和指针(包括智能指针)。
  • Param:指向将被包装的成员的指针
  • 案例:
    #include <functional>
    #include <iostream>
    #include <memory>
     
    struct Foo {
        void display_greeting() {
            std::cout << "Hello, world.\n";
        }
        void display_number(int i) {
            std::cout << "number: " << i << '\n';
        }
        int add_xy(int x, int y) {
            return data + x + y;
        }
        template <typename... Args> int add_many(Args... args) {
            return data + (args + ...);
        }
        auto add_them(auto... args) {
            return data + (args + ...);
        }
     
        int data = 7;
    };
     
    int main() {
        auto f = Foo{};
     
        auto greet = std::mem_fn(&Foo::display_greeting);
        greet(f);  // out:    hello,world
     
        auto print_num = std::mem_fn(&Foo::display_number);
        print_num(f, 42);	// out:   number: 42
     
        auto access_data = std::mem_fn(&Foo::data);
        std::cout << "data: " << access_data(f) << '\n';
     	// output:   data: 7
     	
        auto add_xy = std::mem_fn(&Foo::add_xy);
        std::cout << "add_xy: " << add_xy(f, 1, 2) << '\n';
     	// output:    output:   add_xy: 10     7+1+2
     
        // Working with smart pointer
        auto u = std::make_unique<Foo>();
        std::cout << "access_data(u): " << access_data(u) << '\n';
        std::cout << "add_xy(u, 1, 2): " << add_xy(u, 1, 2) << '\n';
        // output:     access_data(u): 7
    	//             add_xy(u, 1, 2): 10
     
        // Working with member function template with parameter pack
        auto add_many = std::mem_fn(&Foo::add_many<short, int, long>);
        std::cout << "add_many(u, ...): " << add_many(u, 1, 2, 3) << '\n';
    	// output:    	add_many(u, ...): 13    7+1+2+3
    	
        auto add_them = std::mem_fn(&Foo::add_them<short, int, float, double>);
        std::cout << "add_them(u, ...): " << add_them(u, 5, 7, 10.0f, 13.0) << '\n';
        // output:      add_them(u, ...): 42    7+5+7+10+13   
    }
    

std::bind

  • std::bind是函数模板(是一个函数),定义在头文件 functional

  • 函数模板绑定为 f 生成一个转发调用包装器。调用此包装器等效于调用 f 并将其一些参数绑定到 args。

    • f:将绑定到某些参数的可调用对象(函数对象、函数指针、函数引用、成员函数指针或数据成员指针)
    • args:要绑定的参数列表,未绑定的参数由命名空间 std::placeholders 的占位符 _1、_2、_3… 替换
  • 案例:

     #include <random>
     #include <iostream>
     #include <memory>
     #include <functional>
      
     void f(int n1, int n2, int n3, const int& n4, int n5)
     {
         std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
     }
      
     int g(int n1)
     {
         return n1;
     }
      
     struct Foo {
         void print_sum(int n1, int n2)
         {
             std::cout << n1+n2 << '\n';
         }
         int data = 10;
     };
      
     int main()
     {
         using namespace std::placeholders;  // for _1, _2, _3...
      
         std::cout << "1) argument reordering and pass-by-reference: ";
         int n = 7;
         // (_1 and _2 are from std::placeholders, and represent future
         // arguments that will be passed to f1)
         auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
         n = 10;
         f1(1, 2, 1001); 
         ///<output:  2 42 1 10 7    第二个参数2+42+第一个参数 1+n的引用10+生成函数模板的n7 			
      
         std::cout << "2) achieving the same effect using a lambda: ";
         n = 7;
         auto lambda = [ncref=std::cref(n), n=n](auto a, auto b, auto /*unused*/) {
             f(b, 42, a, ncref, n);
         };
         n = 10;
         lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001)
        ///<output:  2 42 1 10 7   第二个参数2+42+第一个参数 1+n的引用10+生成函数模板的n7 		
    
         std::cout << "3) nested bind subexpressions share the placeholders: ";
         auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
         f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);
      	  ///<output: 12 12 12 4 5
      
         std::cout << "4) bind a RNG with a distribution: ";
         std::default_random_engine e;
         std::uniform_int_distribution<> d(0, 10);
         auto rnd = std::bind(d, e); // a copy of e is stored in rnd
         for(int n=0; n<10; ++n)
             std::cout << rnd() << ' ';
         std::cout << '\n';
         ///< 随机输出 0-10的十个数。
      
         std::cout << "5) bind to a pointer to member function: ";
         Foo foo;
         auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
         f3(5);
      	  ///< 100   95+第一个参数5
      	
         std::cout << "6) bind to a mem_fn that is a pointer to member function: ";
         auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
         auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
         f4(5);
         ///<  100   95+第一个参数5
      
         std::cout << "7) bind to a pointer to data member: ";
         auto f5 = std::bind(&Foo::data, _1);
         std::cout << f5(foo) << '\n';
         ///<  10    绑定到指向数据成员的指针
      
         std::cout << "8) bind to a mem_fn that is a pointer to data member: ";
         auto ptr_to_data = std::mem_fn(&Foo::data);
         auto f6 = std::bind(ptr_to_data, _1);
         std::cout << f6(foo) << '\n';
         ///<  10    绑定到作为数据成员指针的 mem_fn
      
         std::cout << "9) use smart pointers to call members of the referenced objects: ";
         std::cout << f6(std::make_shared<Foo>(foo)) << ' '
                   << f6(std::make_unique<Foo>(foo)) << '\n';
         ///< 10  10     使用智能指针来调用被引用对象的成员
     }
    
  • 输出:

       1) argument reordering and pass-by-reference: 2 42 1 10 7
       2) achieving the same effect using a lambda: 2 42 1 10 7
       3) nested bind subexpressions share the placeholders: 12 12 12 4 5
       4) bind a RNG with a distribution: 0 1 8 5 5 2 0 7 7 10 
       5) bind to a pointer to member function: 100
       6) bind to a mem_fn that is a pointer to member function: 100
       7) bind to a pointer to data member: 10
       8) bind to a mem_fn that is a pointer to data member: 10
       9) use smart pointers to call members of the referenced objects: 10 10
    

Lambda 表达式

  • C++11 及更高版本中,lambda 表达式(通常称为 lambda)是一种简便方法,用于定义匿名函数对象 (在作为参数传递给函数的位置 ) 一个 闭包) 。 通常,lambda 用于封装传递给算法或异步函数的一些代码行。

  • 案例: 作为第三个参数传递给 std::sort() 函数

    #include <algorithm>
    #include <cmath>
    
    void abssort(float* x, unsigned n) {
        std::sort(x, x + n,
            // Lambda expression begins
            [](float a, float b) {
                return (std::abs(a) < std::abs(b));
            } // end of lambda expression
        );
    }
    
  • lambda 的组成部分:

    • [capture list] (params list) mutable exception-> return type { function body }
    • capture list:捕获外部变量列表。
      • Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)的数据时以什么方式捕获的,
      • ‘&’表示一引用的方式;
      • ‘=’表明以值传递的方式捕获,除非专门指出。
    • params list:形参列表
    • mutable指示符:用来说用是否可以修改捕获的变量
    • exception:异常设定,异常标识
    • return type:返回类型
    • function body:“函数体”,也就是lambda表达式需要进行的实际操作

    案例:

    int main() // C4297 expected
    {
       []() noexcept { throw 5; }();
    }
    
  • 可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:

    • [capture list] (params list) -> return type {function body}
    • [capture list] (params list) {function body}
    • [capture list] {function body}
  • 捕获外部变量

    • Lambda表达式可以使用其可见范围内的外部变量,但必须明确声明(明确声明哪些外部变量可以被该Lambda表达式使用)。那么,在哪里指定这些外部变量呢?
    • Lambda表达式通过在最前面的方括号[]来明确指明其内部可以访问的外部变量,这一过程也称过Lambda表达式“捕获”了外部变量。
    #include <iostream>
    using namespace std;
    
    int main()
    {
        int a = 123;
        auto f = [a] { cout << a << endl; }; 
        f(); // 输出:123
    
        //或通过“函数体”后面的‘()’传入参数
        auto x = [](int a){cout << a << endl;}(123); 
    }
    
    • 上面这个例子先声明了一个整型变量a,然后再创建Lambda表达式,该表达式“捕获”了a变量,这样在Lambda表达式函数体中就可以获得该变量的值。

    • 类似参数传递方式(值传递、引入传递、指针传递),在Lambda表达式中,外部变量的捕获方式也有值捕获、引用捕获、隐式捕获。

    • 值捕获:

      • 值捕获和参数传递中的值传递类似,被捕获的变量的值在Lambda表达式创建时通过值拷贝的方式传入,因此随后对该变量的修改不会影响影响Lambda表达式中的值。
      int main()
      {
          int a = 123;
          auto f = [a] { cout << a << endl; }; 
          a = 321;
          f(); // 输出:123
      }
      

      这里需要注意的是,如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值。

    • 引用捕获

      • 使用引用捕获一个外部变量,只需要在捕获列表变量前面加上一个引用说明符&
      int main()
      {
          int a = 123;
          auto f = [&a] { cout << a << endl; }; 
          a = 321;
          f(); // 输出:321
      }
      

      引用捕获的变量使用的实际上就是该引用所绑定的对象。

    • 隐式捕获

      • 上面的值捕获和引用捕获都需要我们在捕获列表中显示列出Lambda表达式中使用的外部变量。除此之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。
      int main()
      {
          int a = 123;
          auto f = [=] { cout << a << endl; };    // 值捕获
          f(); // 输出:123
      }
      /// 上下两组都为隐式捕获
      int main()
      {
          int a = 123;
          auto f = [&] { cout << a << endl; };    // 引用捕获
          a = 321;
          f(); // 输出:321
      }
      
  • Lambda表达式的参数

    • 在Lambda表达式中传递参数还有一些限制,主要有以下几点:
      • 参数列表中不能有默认参数
      • 不支持可变参数
      • 所有参数必须有参数名

    这个Eg:

    int m = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5);
    std::cout << "m:" << m << std::endl;    // 输出  m:16
    
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:14:26  更:2022-03-12 17:17:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:24:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码