可調用對象

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

持續學習...

相關文章
相關標籤/搜索