C++ std::function 用法與範例

本篇介紹 C++ 的 std::function 的用法教學,std::function 是 C++11 中加入的一個通用函數包裝器,它能夠將函數、函數物件(可調用物件)以及 Lambda 表達式等作為參數,儲存到一個物件中,並隨時調用這個函數。本文將說明 std::function 的基本用法及範例,詳情請繼續往下閱讀。

要使用 std::function 需要引入的標頭檔<functional>

std::function 基本用法

以下是 std::function 的基本用法及範例,在這個範例中,我們首先定義了一個名為 add 的函數,它接受兩個整數參數並回傳它們的總和。然後我們使用 std::function 來定義了一個函數物件 func,並將其初始化為指向 add 函數的指標。最後,我們通過函數物件 func 來調用 add 函數,傳遞兩個整數參數,並輸出它們的總和。

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

// 定義一個函數,接受兩個 int 參數並回傳它們的總和
int add(int a, int b) {
return a + b;
}

int main() {
// 定義一個 std::function 物件,指向 add 函數
std::function<int(int, int)> func = add;

// 使用函數物件調用 add 函數
std::cout << "Sum: " << func(3, 4) << std::endl;

return 0;
}

結果輸入如下,

1
Sum: 7

使用 Lambda 表達式

在這個範例中,我們定義了一個匿名 Lambda 函數,它接受兩個整數參數並回傳它們的積。然後,我們使用 std::function 來定義了一個函數物件 func,並將其初始化為這個 Lambda 函數。最後,我們通過函數物件 func 來調用 Lambda 函數,傳遞兩個整數參數,並輸出它們的相乘結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <functional>

int main() {
// 定義一個 std::function 物件,使用 Lambda 表達式
std::function<int(int, int)> func = [](int a, int b) {
return a * b;
};

// 使用函數物件調用 Lambda 函數
std::cout << "Product: " << func(3, 4) << std::endl;

return 0;
}

結果輸入如下,

1
Product: 12

std::function 作為函數參數傳遞

在這個範例中,我們定義了一個名為 process 的函數,它接受一個 std::function 物件作為參數,以及兩個整數參數 ab。在 main 函數中,我們使用 Lambda 表達式來建立一個函數物件,並將其作為參數傳遞給 process 函數。在 process 函數內部,我們通過傳入的函數物件來執行相應的操作,並輸出結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <functional>

// 函數接受一個函數物件作為參數
void process(std::function<int(int, int)> func, int a, int b) {
std::cout << "Result: " << func(a, b) << std::endl;
}

int main() {
// 使用 Lambda 表達式作為函數參數
process([](int a, int b) { return a / b; }, 6, 2);

return 0;
}

結果輸入如下,

1
Result: 3

std::function 提供了一個方便的方法來處理函數指標、函數物件和 Lambda 表達式,使得 C++ 中的函數操作更加靈活和方便。

在實務上也常用到 std::function 來做 callback 回調函式的的,詳細請見下範例,

用 std::function 取代傳統的 function pointer

讓我們先來看看一段使用傳統的 function pointer 的範例,

function-pointer-callback.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// g++ function-pointer-callback.cpp -o a.out
#include <iostream>
#include <string>
using namespace std;

void keyevent(int keycode, int status) {
cout << "keycode = " << keycode << " status = " << status << endl;
}

int main() {
void (*callback_keyevent)(int, int) = NULL;
callback_keyevent = keyevent;

if (callback_keyevent)
callback_keyevent(1, 0);

return 0;
}

改成 std::function 後變成這樣

std-function-callback.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ std-function-callback.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <functional>
using namespace std;

void keyevent(int keycode, int status) {
cout << "keycode = " << keycode << " status = " << status << endl;
}

int main() {
std::function<void(int, int)> callback_keyevent = nullptr;
callback_keyevent = keyevent;

if (callback_keyevent)
callback_keyevent(1, 0);

return 0;
}

使用 std::function 的優點

使用 std::function 取代傳統的函數指標(function pointer)有幾個優點:

  1. 可讀性佳:使用 std::function 可以使程式碼更易讀和理解。當你看到一個 std::function 物件時,你立即知道它是一個函數物件,並且可以通過調用該物件來執行相應的操作。這比看到函數指標或其他較為抽象的函數表示形式更直觀。

  2. 可接受 Lambda 函式std::function 可以輕鬆地接受 Lambda 表達式,這使得程式碼更加現代化和易於閱讀。Lambda 表達式通常用於定義簡單的、局部的函數行為,而 std::function 的能力可以讓你將這些 Lambda 表達式作為函數物件使用,從而使程式碼更加簡潔和易於理解。

  3. 更靈活的函數封裝std::function 可以封裝函數指標、函數物件、Lambda 表達式等,使得函數的使用更加靈活。這意味著你可以使用 std::function 來處理更多類型的函數。

  4. 更容易進行函數替換:由於 std::function 可以接受不同類型的函數物件,因此當你需要替換函數時,只需要將新的函數賦值給 std::function 物件即可,而無需修改函數指標的類型。

  5. 不受函數簽名限制:使用函數指標時,你必須確保函數指標的類型與所指向函數的簽名完全匹配。但是,使用 std::function 可以輕鬆地接受不同簽名的函數,從而提供了更大的彈性。

  6. 提供了函數物件的拷貝和賦值能力std::function 物件可以被拷貝和賦值,這使得函數的傳遞和儲存更加方便。

總體來說,std::function 提供了一個更加現代化、靈活且安全的方法來處理函數,並且在許多場景下更加方便和易於使用。

以上就是 C++ std::function 用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
[1] std::function - cppreference.com
https://en.cppreference.com/w/cpp/utility/functional/function
[2] function::function - C++ Reference
http://www.cplusplus.com/reference/functional/function/function/
[3] C++11 std::function的用法 - 滴酱的个人空间 - OSCHINA
https://my.oschina.net/u/2274890/blog/499159
[4] C++11 std::function用法-码农场
https://www.hankcs.com/program/cpp/c11-std-function-usage.html
[5] Cocos2d-x 的 onKeyPressed 與 onKeyReleased
https://github.com/cocos2d/cocos2d-x/blob/e3438ed3fd10a304b7ca2cd3dad9b29fead818d2/cocos/base/CCEventListenerKeyboard.h
https://github.com/cocos2d/cocos2d-x/blob/e3438ed3fd10a304b7ca2cd3dad9b29fead818d2/cocos/base/CCEventListenerKeyboard.cpp
https://github.com/cocos2d/cocos2d-x/blob/e3438ed3fd10a304b7ca2cd3dad9b29fead818d2/tests/performance-tests/Classes/tests/PerformanceEventDispatcherTest.cpp
cocos2d-x 在 PerformanceEventDispatcherTest.cpp 的 KeyboardEventDispatchingPerfTest::generateTestFunctions 使用 onKeyPressed 接收一個 lambda 函式
[6] 邁向王者的旅途: 簡介 std::function (C++11 後的新功能)
https://shininglionking.blogspot.com/2017/01/stdfunction-c11.html
[7] Should I use std::function or a function pointer in C++? - Stack Overflow
https://stackoverflow.com/questions/25848690/should-i-use-stdfunction-or-a-function-pointer-in-c
這篇討論提到應該盡量使用 std::function 來取代 function pointer,除非有什麼其他特殊原因
[8] 在 C++ 裡傳遞、儲存函式 Part 3:Function Object in TR1 – Heresy’s Space
https://kheresy.wordpress.com/2010/11/12/function_object_tr1/

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::thread 用法與範例
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
C++ virtual 的兩種用法
C/C++ 判斷檔案是否存在
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別