仿函數:行為像函數的物件。核心需求:可像函數一樣被呼叫 obj(args...)。三種主要形式:
operator() (傳統仿函數)C++11 後 Lambda 表達式 是最常用形式,本質是編譯器生成的仿函數類別。
struct Adder {
int base_;
explicit Adder(int base) : base_(base) {}
int operator()(int x) const { return base_ + x; } // const 可用於 const 物件
};
Adder add5(5);
add5(10); // 15,像函數呼叫
// 狀態保存在 base_ 中,每個物件可有不同 base
int add(int a, int b) { return a + b; }
using FuncPtr = int(*)(int, int);
FuncPtr fp = add;
fp(3, 4); // 7
// std::function 類型擦除容器 (C++11)
#include
std::function f1 = add; // 函數指標
std::function f2 = [](int a,int b){ return a+b; }; // Lambda
std::function f3 = Adder(10); // 仿函數物件 (需可複製)
f1(1,2); f2(1,2); f3(1,2); // 統一介面調用
std::invoke 統一調用)
struct Calculator {
int multiply(int a, int b) const { return a * b; }
};
Calculator calc;
auto mem_fn = &Calculator::multiply;
(calc.*mem_fn)(6, 7); // 42,語法晦澀
std::invoke(mem_fn, calc, 6, 7); // 42,C++17 統一調用語法
std::invoke([](int a,int b){ return a+b; }, 3, 4); // 7,通用
#include
#include
#include
std::vector v = {5, 2, 8, 1, 9};
// for_each: 對每個元素應用仿函數
std::for_each(v.begin(), v.end(), [](int& x){ x *= 2; });
// sort: 自定義比較仿函數
std::sort(v.begin(), v.end(), std::greater<>()); // 降序
// transform: 一元/二元仿函數轉換
std::vector out(v.size());
std::transform(v.begin(), v.end(), out.begin(), [](int x){ return x*x; });
// find_if: 謂詞仿函數
auto it = std::find_if(v.begin(), v.end(), [](int x){ return x > 20; });
// partition: 謂詞分割
std::partition(v.begin(), v.end(), [](int x){ return x % 2 == 0; });
// 計數器仿函數:帶狀態
struct Counter {
int count_ = 0;
void operator()(int) { ++count_; }
};
Counter c;
std::for_each(v.begin(), v.end(), c);
// c.count_ 現在等於 v.size()
// 函數指標/Lambda (無捕獲) 無法直接保持狀態
| 場景 | 推薦形式 | 理由 |
|---|---|---|
| 簡單邏輯、就地使用 | Lambda (無捕獲/值捕獲) | 語法簡潔、內聯優化最好、無類型擦除開銷 |
| 需跨函數傳遞、儲存、多型 | std::function | 類型擦除統一介面、支援任意可呼叫物件 |
| 高效能、熱路徑、模板算法 | 模板參數 + Lambda/仿函數類別 | 編譯期單態化、零開銷、內聯優化 |
| 需攜帶可變狀態 | 仿函數類別 / Lambda (可變捕獲) | 狀態封裝在物件中、生命週期可控 |
| 介面/回調/插件系統 | std::function / 概念 (C++20) | 二進位介面穩定 / 編譯期多型 |