文章

c++11 仿函数和lambda函数

modern c++ lambda_function

c++11_仿函数和lambda函数

c语言中的函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

void func_hello() {
	printf("hello\n");
}

void func_world() {
	printf("world\n");
}

void func_twice(void (*func)()) {
	func();
	func();
}

int main () {
	func_twice(func_hello);
	func_twice(func_world);
}

缺点

  • 作为参数的函数只能写成全局函数,如果要在main函数里就地生成一个函数就非常不方便
  • 无法访问main里的局部变量
    • 解决方案,在函数参数里加一个void *
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      
      #include <iostream>
      
      void func_hello(void *arg) {
          int num = *(int*)arg;
          printf("hello: %d\n", num);
      }
      
      void func_world(void *arg) {
          printf("world\n");
      }
      
      typedef void (*func)(void *arg);
      
      void func_twice(func func_, void *arg) {
          func_(arg);
          func_(arg);
      }
      
      int main () {
          int x;
          std::cin >> x;
          func_twice(func_hello, &x);
          func_twice(func_world, NULL);
      }
      
    • 如何传递多个参数进去呢?我们可以使用结构体
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      
      #include <iostream>
      
      struct args_t {
          int x;
          int y;
      };
      
      typedef void (*func)(void *arg);
      
      void print_num (void *arg) {
          auto a = (args_t*)arg;
          printf("numbers are: %d %d", a->x, a->y);
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          args_t a;
          a.x = x;
          a.y = y;
          print_num(&a);
      }
      
    • 现在我们又想每次输入变量时不需要重新填充结构体就能使用新的变量那该怎么办呢?结构体里的变量用指针表示就好啦
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      
      #include <iostream>
      
      struct args_t {
          int* x;
          int* y;
      };
      
      typedef void (*func)(void *arg);
      
      void print_num (void *arg) {
          auto a = (args_t*)arg;
          printf("numbers are: %d %d\n", *a->x, *a->y);
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          args_t a;
          a.x = &x;
          a.y = &y;
          print_num(&a);
          std::cin >> x >> y;
          print_num(&a);
      }
      
    • 我们用c++的引用的方式再写一遍
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      
      #include <iostream>
      
      struct args_t {
          int& x;
          int& y;
      };
      
      typedef void (*func)(void *arg);
      
      void print_num (void *arg) {
          auto a = (args_t*)arg;
          printf("numbers are: %d %d\n", a->x, a->y);
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          args_t a{x, y};
          print_num(&a);
          std::cin >> x >> y;
          print_num(&a);
      }
      
    • 为什么说c++不需要捕获参数 arg 呢?答案是参数烙印在了函数类型中了。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      
      #include <iostream>
      
      struct args_t {
          void call() {
              printf("numbers are: %d %d\n", x, y);
          }
          int& x;
          int& y;
      };
      
      typedef void (*func)(void *arg);
      
      template<typename Fn>
      void repeat_twice(Fn fn) {
          fn.call();
          fn.call();
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          args_t a{x, y};
          repeat_twice(a);
          std::cin >> x >> y;
          repeat_twice(a);
      }
      
    • 改进一些的写法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      
      #include <iostream>
      
      struct func_print_args_t {
          void operator() () const{
              printf("numbers are: %d %d\n", x, y);
          }
          int& x;
          int& y;
      };
      
      typedef void (*func)(void *arg);
      
      template<typename Fn>
      void repeat_twice(Fn const& fn) {
          fn();
          fn();
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          func_print_args_t a{x, y};
              
          repeat_twice(a);
          std::cin >> x >> y;
          repeat_twice(a);
      }
      

      实际上lambda函数就类似于上面的那种写法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      struct func_print_args_t {
          void operator() () const{
              printf("numbers are: %d %d\n", x, y);
          }
          int& x;
          int& y;
      };
      
      func_print_args_t a{x, y};    
      repeat_twice(a);
      
      // lambda
      repeat_twice([&x, &y] () {
          printf("numbers are: %d %d\n", x, y);
      });
      
      • lambda 函数会自动加上const修饰
      • 里面的&x可以想象成结构体中auto &x
      • 也有不按引用捕获的方式,就是[x, &y],有时候我们需要在lambda函数里修改x的值,直接修改是会报错的(因为const),我们加上mutable就能解决问题,但是修改y的值是可以的,因为我们捕获的是y的引用。
      • [&] 自动按引用捕获。
      • [=] 自动按值捕获
    • 一个传递参数的小例子
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      
      #include <iostream>
      
      struct func_print_args_t {
          void operator() (int i) const{
              printf("#%d: numbers are: %d %d\n",i, x, y);
          }
          int& x;
          int& y;
      };
      
      typedef void (*func)(void *arg);
      
      template<typename Fn>
      void repeat_twice(Fn const& fn) {
          fn(1);
          fn(2);
      }
      
      int main () {
          int x, y;
          std::cin >> x >> y;
          func_print_args_t a{x, y};
              
          repeat_twice(a);
          std::cin >> x >> y;
          repeat_twice(a);
          repeat_twice([&x, &y] (int i) {
              printf("#%d: numbers are: %d %d\n", i, x, y);
          });
      }
      
本文由作者按照 CC BY 4.0 进行授权

热门标签