可调用对象

2021年01月14日 阅读数:4
这篇文章主要向大家介绍可调用对象,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
A successful book is not made of what is in it, but what is left out of it.
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ ㅤㅤㅤ— Mark Twain

接触C++ 2.0已经有段时间了,简单总结一下C++中认为是函数的东西,或者说相似于函数的东西,咱们从标准的C和C++函数到函数对象和lambda表达式慢慢讲起。c++

1. 常规函数

C++中定义函数的方式有不少,下面咱们举例说明,对于一些耳熟能详的概念就一带而过。编程

  • 标准C函数
bool greater(int arg1, int arg2) { return arg1 > arg2; }
  • 类成员函数
struct number {
  bool greater(int arg1, int arg2) { return arg1 > arg2; }
};

说明一下,在C++中我通常只使用struct来定义类,而不是使用传统的class,后面我全部的文字都会如此,这纯粹是我的的编程风格,读者只要保持本身的习惯,并一如既往坚持下去就行。函数

  • 类静态函数
struct number {
  static bool greater(int arg1, int arg2) { return arg1 > arg2; }
};

前面关于普通的函数,类的成员函数以及类的静态方法,相信读者已经耳熟能详了,下面介绍一下C++ 2.0的新式函数定义。学习

  • C++ 2.0的新式函数

c++11提供了一种新式函数的写法,在函数的末尾说明函数的返回类型,函数开头使用auto关键字,这一用法主要用于编写函数模板。this

// C++ 11
auto greater(int arg1, int arg2) -> bool {    /* 尾置返回类型 */
    return arg1 > arg2;
}

上面的用法并不经常使用,在c++14之后能够彻底忽略返回值类型,而由编译器根据return语句自动推断,这里的牵涉内容比较多,就不详细展开了,简单举两个例子。指针

// C++ 14
int value = 3;
auto answer()  { return value ; }     /* 返回类型 int */
const auto& answer()  { return value ; }     /* 返回类型 const int& */

// 还能够使用 decltype 关键字
decltype(auto) answer()  { return value ; }

2. 函数指针

函数指针(function pointer)它是一个存放函数地址的变量,能够经过这个变量调用该函数。在c++11以前通常使用typedef 关键字去定义函数指针类型,在c++11以后能够使用更具表现力的using来替代。c++11

bool greater(int arg1, int arg2) { return arg1 > arg2; }

// C++11之前
typedef bool (*old_cmp)(int, int);
old_cmp cmp = greater;

// C++11之后
using new_cmp = bool (*)(int, int);
new_cmp  cmp = greater;

3. 仿函数

其实,在C++中一直能够定义和使用像函数同样的对象,它们被称为仿函数(Functor)。本质上,就是一个重载的调用操做符的类(call operator),即定义了operator()的类,能够有任意个数和任意类型的参数。code

struct functor { 
    return_type operator()(args...) const { ... }
};

另外,值得提一下的是,根据operator()包含的0个、1个或2个参数,这种Functor分别被称为生成器、一元仿函数或二元仿函数,下面分别举例说明。对象

  • 生成器
struct increase_generator { 
  int operator()() noexcept { return num++; }
private:
  int num = 0;
};

工做原理很是简单,每次调用increase_generator::operator()时,将成员变量num的值返回,并将num的值增长1。作用域

int main() {
  increase_generator num_generator;
  for (int i = 0; i < 3; ++i) {
    std::cout << num_generator() << std::endl;
  }
}
// output: 0 1 2
  • 一元仿函数
struct cube { 
  constexpr int operator()(const int value) const noexcept { return value * value * value; }
};

顾名思义,这个仿函数对它传递的值作了立方运算,而且这个operator()被声明为const,它的行为相似于数学上的纯函数,即无反作用。这里constexpr的做用,有兴趣的读者能够自行去研究一下。

  • 谓词

一元仿函数一个经常使用的用途就是当作谓词(predict),即只有一个参数且返回值为bool类型的仿函数。以下:

struct is_even { 
  constexpr bool operator()(const int value) const noexcept { return (value % 2) == 0; }
};

举个使用的例子

int main() {
  std::vector<int> numbers{1, 2, 3, 4, 5};
  numbers.erase(std::remove_if(std::begin(numbers), std::end(numbers), is_even()), 
                std::end(numbers));

  std::copy(std::begin(numbers), std::end(numbers), std::ostream_iterator<int>{std::cout, " "});
  return 0;
}
// output: 1 3 5 

上面的示例使用了Erase-remove惯用法,结合咱们定义的is_even仿函数,实现了对vector中偶数元素的删除。

  • 二元仿函数
struct greater { 
  bool operator()(const auto& v1, const auto& v2) const noexcept { return v1 > v2; }
};

4. lambda

  • 看个例子

咱们把上面实现的仿函数is_even,一样的用lambda去实现,以下

auto is_even = [] (auto item) { return (item % 2) == 0; };

is_even(2);  // 返回true

能够看到,使用lambda的实现更加简短,表现力也更丰富,不过一般lambda表达式使用都会内联实现,即在应用时实现。
注:上面的语法须要支持C++14及以上的编译器才能够编译成功。

  • 语法
[capture list] (param list) -> return_type { lambda body;}

说明

  • [capture list] 捕获列表,用于捕获外层变量

[] 不捕获任何变量
[&] 捕获外部做用域中全部变量,并做为引用在匿名函数体中使用
[=] 捕获外部做用域中全部变量,并拷贝一份在匿名函数体中使用
[x, &y] x按值捕获, y按引用捕获
[&, x] x按值捕获. 其它变量按引用捕获
[=, &y] y按引用捕获. 其它变量按值捕获
[this] 捕获当前类中的this指针,若是已经使用了&或者=就默认添加此选项

  • (param list) 参数列表

当匿名函数没有参数时,能够省略(param list)部分

  • -> return_type 返回值类型

C++14之后能够省略

  • { lambda body;} 函数实现

5. std::function包装函数对象

std::function 是一个可调用对象包装器,是一个类模板,能够容纳上述全部的可调用对象,它能够用统一的方式处理函数、函数对象、函数指针,并容许保存和延迟它们的执行。

  • 做用

对函数指针(包括普通函数,类成员函数,类静态成员函数),仿函数,lambda表达式作类型消除,也就是说能够将这些可调用实体都转换成std::function类型

  • 示例

定义格式std::function<函数类型>

bool greater_global(int arg1, int arg2) { return arg1 > arg2; }   // 普通函数

struct number {
  bool greater_member(int arg1, int arg2) { return arg1 > arg2; }  // 类成员函数
  static bool greater_static(int arg1, int arg2) { return arg1 > arg2; } // 类静态函数
};

// 仿函数
struct greater_functor { 
  bool operator()(int arg1, int  arg2) const noexcept { return arg1> arg2; }
};

auto greater_lambda = [] (int arg1, int arg2) { return arg1 > arg2; };  // lambda
auto greater_lambda2 = [] (auto arg1, auto arg2) { return arg1 > arg2; };  // 通用 lambda

int main() {
  std::function<bool(int, int)> test_function;
  test_function = greater_global;

  number object;
  test_function = std::bind(&number::greater_member, &object, 
                            std::placeholders::_1, std::placeholders::_2);
  test_function = std::bind(&number::greater_static, 
                            std::placeholders::_1, std::placeholders::_2);

  test_function = greater_lambda ;
  test_function = greater_lambda2;
  test_function = greater_functor();
  
  test_function(3, 2);
}

6. End

持续学习...