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

C++ 仿函數詳解:從 STL 到 Loki 的泛型編程精髓

     編輯:關於C++

仿函數是 C++ 泛型編程的基石,它讓物件擁有函數般的呼叫語法,並能攜帶狀態、支援型別推導、無縫融入 STL 演算法。本文梳理 STL 兩大基底類別的區別、實戰範例,並引入 Loki 庫的 Functor 與 Typelist,帶你窺探模板遞歸的極致之美。

一、STL 兩大仿函數基底類別

基底類別參數個數適用場景典型成員型別
std::unary_function<Arg, Result>1單引數演算法(如 find_if, transform)argument_type, result_type
std::binary_function<Arg1, Arg2, Result>2雙引數演算法(如 sort, accumulate)first_argument_type, second_argument_type, result_type

繼承這些基底類別不強制,但提供統一的型別別名,方便演算法內部萃取參數/回傳型別(C++11 起已棄用,改用 std::invoke_result 等特徵)。

二、實戰範例

1. 一元仿函數:門檻判斷

template <typename T>
class MeetsThreshold : public std::unary_function<Widget, bool> {
    const T threshold_;
public:
    explicit MeetsThreshold(const T& t) : threshold_(t) {}
    bool operator()(const Widget& w) const {
        return w.value() > threshold_;
    }
};
// 用法:std::find_if(v.begin(), v.end(), MeetsThreshold<int>(42));

2. 二元仿函數:Widget 名稱比較

struct WidgetNameCompare : public std::binary_function<Widget, Widget, bool> {
    bool operator()(const Widget& lhs, const Widget& rhs) const {
        return lhs.name() < rhs.name();
    }
};
// 用法:std::sort(v.begin(), v.end(), WidgetNameCompare());

三、現代 C++ 的演進:Lambda 與 std::function

C++11 起,Lambda 表達式已成為最常用的仿函數形式:

auto cmp = [](const Widget& a, const Widget& b) { return a.name() < b.name(); };
std::sort(v.begin(), v.end(), cmp);

需攜帶狀態、跨編譯單元傳遞或類型擦除時,改用 std::function<bool(const Widget&, const Widget&)>。

四、Loki 庫的 Functor 與 Typelist:模板遞歸的極致

Andrei Alexandrescu 的 Loki 庫實現了通用的 Functor,支援:

  • 任意參數個數(至 15 個以上)
  • 綁定成員函數、自由函數、仿函數物件
  • 參數綁定、柯里化、回傳型別推導

其核心機制是 Typelist(型別清單)——一個編譯期遞歸結構:

template <class Head, class Tail> struct Typelist { typedef Head Head; typedef Tail Tail; };
struct NullType {}; // 終止符

配合模板特化與遞歸,實現參數包的展開、型別轉換、函數簽名匹配等編譯期計算。這正是「現代 C++ 設計」中模板元編程的經典範例,建議下載 Loki 源碼研讀 Functor.h 與 Typelist.h。

五、最佳實踐清單

  1. 優先使用 Lambda;需持久化/跨 DLL 才用 std::function。
  2. 避免繼承 unary_function/binary_function(C++17 移除),改用型別特徵。
  3. 仿函數應為 const 正確:operator() 標記 const。
  4. 大型仿函數物件傳遞時考慮 std::ref 避免拷貝開銷。

從 STL 簡潔的基底類別,到 Loki 龐大的模板機器,仿函數見證了 C++ 泛型編程從「型別萃取」走向「編譯期計算」的演進。掌握其精髓,能讓你在架構決策時遊刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved