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

C++11 新特性深度解析:std::function 與回調函數完整指南

     編輯:關於C++

基本用法:綁定與調用

std::function 是可調用對象的類型擦除包裝器,支持函數指針、仿函數、Lambda、std::bind 綁定結果等。


#include <functional>
#include <iostream>
using cb1_t = std::function<void()>;
using cb2_t = std::function<void(int)>;

void foo1() { std::cout << "foo1 called\n"; }
void foo2(int i) { std::cout << "foo2 called with: " << i << "\n"; }
struct S { void foo3() { std::cout << "foo3 called.\n"; } };

int main() {
    // 綁定自由函數
    cb1_t f1 = std::bind(&foo1); f1();
    // 綁定帶參數函數(參數綁定)
    cb1_t f2 = std::bind(&foo2, 5); f2();
    // 綁定佔位符(參數延遲綁定)
    cb2_t f3 = std::bind(&foo2, std::placeholders::_1); f3(42);
    // 綁定成員函數
    S s; cb1_t f4 = std::bind(&S::foo3, &s); f4();
    // 綁定 Lambda
    cb1_t f5 = std::bind([] { std::cout << "lambda called\n"; }); f5();
    return 0;
}

存儲回調:異構容器難題

std::vector<std::function<void()>> 可存儲同簽名回調。異構簽名需用 std::variant (C++17) 或類型擦除基類。


using cb1_t = std::function<void()>;
using callbacks_t = std::vector<cb1_t>;
callbacks_t callbacks;

void foo1() { std::cout << "foo1\n"; }
void foo2(int i) { std::cout << "foo2: " << i << "\n"; }

int main() {
    callbacks.push_back(std::bind(&foo1));
    callbacks.push_back(std::bind(&foo2, 5)); // 參數靜態綁定
    int n = 15;
    callbacks.push_back(std::bind(&foo2, std::cref(n))); // 參數運行時綁定
    for (auto& f : callbacks) f();
    return 0;
}

核心進階:包裝函數

將任意可調用對象包裝為 std::function 作為函數參數,統一接口。提供多重重載支持:

  • 已是 std::function 直接調用
  • 普通可調用對象 → 隱式構造 std::function 再調用
  • 函數指針(如無捕獲 Lambda)→ 專門重載避免 std::function 開銷

#include <functional>
#include <iostream>

using cb1_t = std::function<void()>;
using cb2_t = std::function<void(int)>;

// 1. 包裝 std::function 無參
template<typename R> void call(std::function<R()> f) { f(); }

// 2. 包裝 std::function 有參
template<typename R, typename... Args> void call(std::function<R(Args...)> f, Args... args) { f(args...); }

// 3. 通用可調用對象無參 → 委托給 1
template<typename R> void call(R (*f)()) { call(std::function<R()>(f)); }

// 4. 通用可調用對象有參 → 委托給 2
template<typename R, typename... Args> void call(R (*f)(Args...), Args... args) { call(std::function<R(Args...)>(f), args...); }

// 5. 函數指針專用(避免 std::function 開銷)
using fp = void(*)();
void call(fp f) { call(std::function<void()>(f)); }

void foo1() { std::cout << "foo1\n"; }
void foo2(int i) { std::cout << "foo2: " << i << "\n"; }

int main() {
    call(&foo1);
    cb1_t f1 = std::bind(&foo1); call(f1);
    call(&foo2, 5);
    cb2_t f2 = std::bind(&foo2, std::placeholders::_1); call(f2, 5);
    call([] { std::cout << "lambda\n"; }); // 調用 fp 重載
    return 0;
}

性能提示

  • std::function 有類型擦除開銷(堆分配、虛函數調用),熱路徑優先用模板/函數指針
  • 小對象優化 (SBO) 可能避免堆分配,但不保證
  • std::bind 比 Lambda 稍慢,現代 C++ 推薦 Lambda + std::function 僅在必要時使用

最佳實踐

  1. 回調存儲:統一簽名用 vector<function<Sig>>
  2. 函數參數:模板接受任意可調用對象,內部按需包裝
  3. 異構回調:C++17 variant<function<Sig1>, function<Sig2>...> 或基類類型擦除
  4. 移動語義:std::function 支持移動,避免拷貝開銷
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved