首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 仿函數完全指南:函數物件、Lambda、std::function 與 STL 應用

     編輯:C++ 入門

什麼是仿函數?

仿函數:行為像函數的物件。核心需求:可像函數一樣被呼叫 obj(args...)。三種主要形式:

  1. 類別重載 operator() (傳統仿函數)
  2. 函數指標 (C 風格)
  3. 成員函數指標 (需物件實例)

C++11 後 Lambda 表達式 是最常用形式,本質是編譯器生成的仿函數類別。

三種形式完整示範

1. 類別仿函數 (有狀態)


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

2. 函數指標 / std::function


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);  // 統一介面調用

3. 成員函數指標 (C++17 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,通用

STL 大量使用仿函數的核心算法


#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 (無捕獲) 無法直接保持狀態

現代 C++ 仿函數最佳實踐

效能對比

  • Lambda/仿函數類別 + 模板:零開銷,編譯器完全內聯優化
  • std::function:類型擦除開銷 (間接呼叫、堆上分配小物件優化 SBO)、約 2-3x 慢於模板
  • 函數指標:間接呼叫,難以內聯,但無額外分配
  • std::invoke:零開銷統一調用語法

C++20 概念 限制仿函數介面


template
concept UnaryPredicate = requires(F f, T t) {
    { f(t) } -> std::convertible_to;
};

template Pred>
void process(std::vector const& v, Pred pred) {
    for (int x : v) if (pred(x)) { /* ... */ }
}
// 編譯期檢查 pred 可呼叫且回傳可轉 bool

仿函數是 C++ 泛型編程的靈魂,從 STL 算法到現代非同步框架、訪問者模式、領域特定語言,掌握其三種形式、狀態封裝、效能特徵與現代語法,是高階 C++ 開發者的核心竅門。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景推薦形式理由
簡單邏輯、就地使用Lambda (無捕獲/值捕獲)語法簡潔、內聯優化最好、無類型擦除開銷
需跨函數傳遞、儲存、多型std::function類型擦除統一介面、支援任意可呼叫物件
高效能、熱路徑、模板算法模板參數 + Lambda/仿函數類別編譯期單態化、零開銷、內聯優化
需攜帶可變狀態仿函數類別 / Lambda (可變捕獲)狀態封裝在物件中、生命週期可控
介面/回調/插件系統std::function / 概念 (C++20)二進位介面穩定 / 編譯期多型