你說的內容是關于 C++ 中 可調用類型(Callable Types) 的基礎知識,我幫你理清并補充理解。
Callable Types(可調用類型)簡介
C++ 中任何可以用 ()
括號操作符“調用”的對象,都叫做 可調用類型。典型包括:
1. 函數指針和函數引用
int (*functionPointer)(std::string); // 指向函數的指針
int (&functionReference)(std::string); // 函數的引用
它們都指向一個可被調用的函數,調用方式如:
int result = functionPointer("hello");
int result2 = functionReference("world");
2. Lambda 表達式
匿名函數對象,語法:
auto lambda = [](std::string s) -> int { return s.size(); };
int result = lambda("test");
lambda 實際上是編譯器生成的匿名類對象,重載了 operator()
。
3. 函數對象(Functor)
你可以自定義一個類或結構體,實現 operator()
,使其實例可以像函數一樣被調用:
class ComplexObject {
public:int operator()(std::string s) {return s.size() * 2; // 舉例}
};
ComplexObject obj;
int result = obj("hello");
4. 補充:static
operator()
注意,你提到的:
class ComplexObject {static int operator()(std::string);
};
這個聲明是不合法的。因為 operator()
必須是實例成員函數(non-static),它的意義就是讓對象可以被調用。
如果你寫 static
,就不能通過對象調用了:
ComplexObject obj;
obj("hello"); // 不行,static operator() 無法用obj調用
你可以寫普通靜態成員函數,但那就不是函數調用運算符重載了。
小結
類型 | 示例 | 說明 |
---|---|---|
函數指針 | int (*fp)(std::string); | 指向普通函數 |
函數引用 | int (&fr)(std::string); | 綁定到函數 |
Lambda 表達式 | auto lambda = [](std::string) { ... }; | 匿名函數對象,重載了 operator() |
函數對象(重載 operator() ) | class Foo { int operator()(std::string); }; | 自定義可調用類型 |
帶狀態的可調用對象(Stateful Callables),讓我幫你詳細解釋和梳理下:
Stateful Callables(有狀態的可調用對象)
“有狀態”指的是這個可調用對象內部保存了數據(狀態),每次調用可能用到這些狀態或改變它們。
1. 捕獲變量的 Lambda(Stateful Lambda)
int x = 5;
auto lambda = [x](std::string s) { return s.size() + x; };
- 這里的
lambda
捕獲了外部變量x
,把它存到 lambda 對象里。 - 每次調用
lambda
,都會用這個x
參與計算。 - 這個 lambda 對象內部有狀態(
x
),所以是 stateful callable。
2. 自定義類或結構體,重載非靜態 operator()
class ComplexObject {int x;
public:ComplexObject(int x_) : x(x_) {}int operator()(std::string s) const {return s.size() + x;}
};
- 這里
ComplexObject
有成員變量x
(狀態) - 重載了
operator()
,通過對象調用時可以用到內部狀態。 - 也是一個 stateful callable。
3. 為什么 operator()
要是非靜態?
operator()
是調用對象的核心,非靜態才能訪問對象的成員變量(狀態)。- 靜態
operator()
沒法訪問對象成員,因此不算真正的“stateful callable”。
4. 總結
類型 | 例子 | 特點 |
---|---|---|
無狀態 Lambda | [](int x){ return x*x; } | 沒捕獲外部變量,純函數 |
有狀態 Lambda | [x](std::string s){ return s.size() + x; } | 捕獲外部變量,內部有狀態 |
函數對象(類/結構體) | 有成員變量的類重載非靜態 operator() | 內部保存狀態,調用時可用 |
這部分講的是 會修改自身狀態的可調用對象(State-Mutating Callables),我幫你詳細說明:
State-Mutating Callables(會改變狀態的可調用對象)
這類 callable 不僅“有狀態”,而且在調用時會修改它們內部的狀態。
1. mutable lambda
默認情況下,捕獲外部變量的 lambda 是 const
調用的,不能修改捕獲的變量副本。
int x = 0;
auto lambda = [x](std::string s) mutable {x += s.size(); // 修改了 lambda 內部捕獲的 x 副本return x;
};
mutable
關鍵字讓 lambda 的operator()
變成非 const,允許修改捕獲的變量副本。- 注意,捕獲的是值拷貝,修改的是 lambda 內部的那個副本,不影響外部
x
。
2. 非 const 的非靜態 operator()
class ComplexObject {int x = 0;
public:int operator()(std::string s) {x += s.size(); // 修改對象的狀態return x;}
};
- 這里
operator()
不是const
,說明調用會改變對象狀態。 - 你可以記錄調用次數、累計某些值等。
3. 區別于前面 const operator()
的地方
const operator()
不允許修改成員變量(除非用mutable
修飾成員變量)- 非 const
operator()
可以修改成員變量,實現狀態變化
4. 總結表
類型 | 示例 | 說明 |
---|---|---|
mutable lambda | [x](std::string s) mutable { x += s.size(); return x; } | 允許修改捕獲變量的副本 |
類的非 const operator() | int operator()(std::string s) { x += s.size(); return x; } | 允許修改對象成員變量,狀態改變 |
你提到的是傳遞可調用對象(callables)給函數的方式,特別是用 函數指針 傳遞的限制,我幫你詳細解釋:
傳遞可調用對象(Passing Callables)
1. 函數指針只能傳遞無狀態的可調用對象
舉例:
std::string work(int x);
void workAsynchronously(int x, void (*processResult)(std::string));
processResult
是一個 函數指針,指向一個void(std::string)
的函數。- 這意味著傳入的回調只能是無狀態的普通函數,不能是有狀態的 lambda 或函數對象。
2. 為什么函數指針只能指向無狀態函數?
- 函數指針實際指向具體的函數地址。
- 有狀態 lambda 和函數對象是對象實例,它們保存狀態,需要調用其成員函數(
operator()
),而不是普通函數地址。 - 因此,函數指針無法表示帶狀態的可調用對象。
3. 如果想傳遞有狀態 callable,該怎么辦?
- 用
std::function
(類型擦除),支持任何可調用對象,包括有狀態的 lambda 和函數對象:
#include <functional>
void workAsynchronously(int x, std::function<void(std::string)> processResult);
- 這樣,傳入的
processResult
可以是:
auto lambda = [capturedData](std::string s) { /*...*/ };
workAsynchronously(5, lambda);
4. 總結
傳遞方式 | 支持的 callable 類型 | 備注 |
---|---|---|
函數指針(void(*)(T) ) | 只能無狀態的普通函數 | 不能傳遞捕獲變量的 lambda |
std::function<T> | 支持所有 callable,包括帶狀態的 lambda 和函數對象 | 靈活但有一定運行時開銷 |
std::function
其實是個 函數包裝器(Function Wrapper),用來封裝各種可調用對象,提供統一調用接口。下面幫你拆解理解:
std::function
簡化結構解析
template<typename R, typename... Args>
class function<R(Args...)> {void* state; // 存儲可調用對象的狀態(捕獲的數據)void (*func)(void*, Args...); // 指向“調用”函數的指針,傳入state和參數void (*destroy)(void*); // 指向銷毀函數,用于清理state指針內存~function(); // 析構時調用destroy清理stateR operator()(Args... args); // 調用包裝的函數對象template<typename F>function(F&& f); // 構造時,把任意可調用對象包裝進來template<typename F>function& operator=(F&& f); // 賦值操作符,重新包裝
};
關鍵點
- void state*
這里保存了任意類型的可調用對象的狀態,比如捕獲的變量、類實例等等。
用void*
實現類型擦除,隱藏了具體類型。 - func 指針
指向一個統一的調用接口,實現了調用時把state
傳進去,再調用對應可調用對象的operator()
。 - destroy 指針
用于析構state
指向的對象,防止內存泄漏。 - 構造函數模板
允許用任意類型的可調用對象(函數指針、lambda、函數對象)來初始化std::function
。 - 調用運算符
讓std::function
實例看起來像普通函數一樣,可以直接用()
調用。
為什么需要這么設計?
- 統一接口:不管是普通函數、帶狀態的 lambda,還是自定義的函數對象,都能用同一種類型變量來保存并調用。
- 類型擦除:隱藏不同可調用對象類型的差異,簡化代碼調用。
- 靈活性和便利性:把多態調用變得簡單。
你提到的是 std::function
的實現細節和其運行時開銷,我來幫你詳細解析這些點,幫助你更好理解:
std::function
的結構(以 libstdc++ 的實現為例)
一個 std::function<R(Args...)>
實例,大約占 48 字節(在 x86_64 上),這些字節大致分配如下:
組成部分 | 說明 |
---|---|
函數調用指針 | 指向調用封裝對象的函數:R (*invoke)(void*, Args...) |
管理函數指針 | 指向處理 copy、destroy、move 的函數:void (*manager)(...) |
內部 buffer(32B) | 小對象優化區,用于直接放入小對象(例如小 lambda) |
或者指針指向堆內對象 | 如果封裝對象較大,就會動態分配在堆上,buffer 存儲的是指針地址 |
特性解析
小對象優化(SBO:Small Buffer Optimization)
- 如果你的 lambda 或函數對象小于 32 字節,就會直接放入內部 buffer,避免堆分配,提高性能。
- 如果太大,就會在堆上創建對象,然后內部 buffer 存放指針。
可復制(copyable)
std::function
是可復制的。- 拷貝會拷貝封裝的對象(調用對應的
copy
管理函數)。
std::function<void()> f1 = [] { std::puts("hi"); };
std::function<void()> f2 = f1; // 拷貝了 lambda 封裝體
不一定 noexcept 可移動
- 移動操作不是
noexcept
,意味著在某些容器(如std::vector
)中移動std::function
可能觸發重新分配或異常傳播。 - 原因:封裝的對象類型可能自帶拋異常的移動構造函數。
示例:大小對比
#include <iostream>
#include <functional>
int main() {std::function<void()> small = [] { std::cout << "small\n"; };auto bigLambda = [buffer = std::array<int, 100>{}] { std::cout << "big\n"; };std::function<void()> big = bigLambda;std::cout << "sizeof(std::function): " << sizeof(std::function<void()>) << "\n";
}
輸出(在 x86_64 上):
sizeof(std::function): 48
總結
特性 | 說明 |
---|---|
大小 | 通常 48 字節(libstdc++) |
SBO | 32 字節內的對象放棧上,否則放堆上 |
可復制 | 會復制被封裝對象 |
移動非 noexcept | 使用時注意異常安全性 |
功能 | 封裝任意可調用對象,延遲調用,多態調用 |
你說的這段是在說明 std::function
的典型使用場景,尤其是它的任務封裝功能。我來幫你逐句解釋:
典型用途解釋
「passing a task to libraries for execution at a later time」
將某個任務(比如 lambda)傳給一個庫,讓它**“以后”執行**,不是現在馬上執行。
例子:傳一個回調到異步網絡庫中。
void startAsync(std::function<void(std::string)> callback);
「or in a different thread」
把任務傳到另一個線程去執行,比如線程池。
例子:
std::thread t([] { doWork(); });
「storing those tasks in the library implementations」
這些任務(lambda、函數等)通常會被**“保存”在庫內部的數據結構里**,等時間合適再執行。
例子:任務隊列
std::queue<std::function<void()>> taskQueue;
「in either case, those tasks are never executed more than once」
這些任務通常只執行一次,執行完就丟棄,沒有重用的需求。
所以不需要支持多次調用,比如:
auto task = [] { std::puts("run once"); };
task(); //
task(); // 通常不會有這個需求
「and there is never a need to copy them」
這些任務傳進去之后,不會被復制。只需要“移動”進庫中,然后庫調用它就結束了。
總結成一句話:
這些“任務式”的可調用對象通常:
- 只執行一次
- 被移動而非復制
- 用于延遲/異步執行
- 常用于線程池、事件循環、任務調度器中
延伸建議:使用 std::function
還是 std::move
或 std::unique_function
如果任務只用一次、無需復制,std::function
有點重。C++23 起可以用 std::move_only_function
(或者第三方的 unique_function
),更輕更高效:
std::move_only_function<void()> task = std::move(lambda);
你這段是對 Facebook 的開源代碼庫(如 Folly)中,std::function
的典型使用場景的描述。我來幫你逐條解釋:
內容逐句解析
folly::Executor* executor;
這是一種抽象接口指針,用來表示一個“任務執行器”。
executor->add(callable);
往這個執行器里“添加”一個任務(可調用對象)。callable
可以是 lambda、函數、綁定對象等。
folly::Executor is an interface (abstract base class) for passing tasks that need to be executed
folly::Executor
是一個接口類(抽象基類),它的職責是:接收并執行異步任務。
相當于你設計了一個標準協議,任何執行器(線程池、事件循環等)都可以實現它。
implementations include a thread pool which executes tasks in parallel
這個接口的具體實現包括:
- 線程池(并行執行)
- 單線程事件循環(串行調度)
- IO 線程(和 Reactor 模式結合)
std::function<void()> was used to pass tasks to the executor
std::function<void()>
是傳給 executor->add()
的參數類型,表示:
“一個可以執行、不帶參數、不返回值的任務”。
這種使用方式讓
folly::Executor
成為一個高層、通用的任務調度器接口。
總結
在 Facebook/Folly 的實踐中:
元素 | 作用 |
---|---|
folly::Executor | 任務執行接口(類似抽象線程池) |
executor->add(task) | 添加一個延遲執行的任務 |
std::function<void()> | 用來封裝傳入的任務 |
設計上的好處
- 任務的來源不受限制(lambda、函數、類)
- 執行方式可以靈活替換(線程池、主線程調度器)
- 接口通用、便于解耦模塊
這段講的是 Facebook Folly 庫中 Future
的常見使用場景,以及它與 std::function
的關系。我來幫你逐句解析 + 總結背后原理:
示例代碼含義
folly::Future<std::string> result = someRpcCall(1, 2, 3);
表示調用了一個異步 RPC 函數,它返回一個 Future<std::string>
,未來會獲得一個 std::string
。
result.then([&foo](std::string r) {return foo.extractNumber(r);
})
給這個 Future
添加一個 .then()
回調:
- 當
result
可用了,就調用 lambda。 - lambda 從字符串里提取出數字(
int
)。
.then([obj = std::move(obj)](int x) {obj.setNumber(x);
});
接著鏈式 .then()
調用,把前一個結果 x
(一個 int
)交給另一個 lambda。
- 這里用了 C++ 的“帶 move capture 的 lambda”
obj
是只在 lambda 中使用的一個局部狀態對象
概念解釋:Future + then 回調機制
Future<T>
:代表未來會得到一個 T
這是一個異步結果占位符。
.then(func)
:
當結果可用時,調用你傳進去的 func(回調)。
就像 JavaScript 的
.then()
,但類型安全,支持 C++ 特性。
回調的存儲方式:使用 std::function
你寫的這句:
“the implementation used to use std::function to store the callback”
表示早期實現是這樣寫的:
std::function<void(T)> callback;
也就是用 std::function
來存儲 then()
傳入的 lambda。這有幾個好處:
優點 | 缺點 |
---|---|
可以存儲任意可調用對象 | 比較重(拷貝 / 類型擦除) |
簡化了接口 | 不支持 move-only 類型 |
后續優化:不再使用 std::function
因為 std::function
不支持:
- move-only lambda(捕獲 unique_ptr 或 std::move(obj))
- noexcept move
- 精確類型推導(性能)
所以 Folly 后來換成了 手寫的輕量 type-erased function wrapper,支持 move-only 語義。類似于:
template<typename T>
struct MoveOnlyCallback {virtual void operator()(T) = 0;virtual ~MoveOnlyCallback() = default;
};
總結
這段代碼展示了:
內容 | 意義 |
---|---|
Future<T> | 管理異步結果 |
.then(callback) | 注冊異步回調 |
回調傳 lambda(可帶狀態) | 支持鏈式操作、異步數據流 |
早期用 std::function 儲存回調 | 簡單但不支持 move-only,后來被優化掉 |
你這段是講 C++ 中使用 std::function
的限制,尤其是它不支持捕獲 move-only 類型的問題,以及 Facebook Folly 提供的一些 解決方法。下面是逐點講解與深入理解:
問題:std::function
不支持 move-only 捕獲
示例問題代碼:
MoveOnlyType x;
executor.add([x = std::move(x)]() mutable { x.doStuff(); });
這在某些實現中無法編譯,原因是:
std::function
只能封裝可復制(copyable)對象。而這個 lambda 捕獲了MoveOnlyType
,它不可復制。
常見 Workaround 1:用 std::shared_ptr
auto x = std::make_shared<MoveOnlyType>();
executor.add([x]() { x->doStuff(); });
優點:
- lambda 可復制了,shared_ptr 也是可復制的
缺點:
- 每次調用都涉及堆分配
- 需要原子操作維護引用計數(性能差)
- 會讓你為避免
std::function
限制而犧牲所有權語義
Workaround 2:用 folly::MoveWrapper<T>
folly::MoveWrapper<MoveOnlyType> x;
executor.add([x]() mutable { x->doStuff(); });
優點:
MoveWrapper<T>
實際上是一個**“偽復制對象”**,在復制時會自動移動內部對象- 這讓 lambda 看起來是可復制的,但實際上把資源從左值轉成了右值傳進來
缺點:
- 違反了 C++ 的復制語義(拷貝其實是 move)
- 很像已經棄用的
std::auto_ptr
—— 危險、易錯 - 一不小心就可能在拷貝時丟失數據
本質問題總結:
項目 | 問題 |
---|---|
std::function | 不能 wrap move-only lambda |
lambda 捕獲 move-only | 會使 lambda 本身不可復制 |
std::function 要求 copyable | 所以編譯報錯 |
workaround(shared_ptr / MoveWrapper) | 都是權衡性能或語義的方案 |
Folly 的真正解決方案(后續)
Folly 的后續優化是引入了一個可以支持 move-only lambda 的輕量函數包裝器(非 std::function
),其特性包括:
- 支持
unique_ptr
、Promise
等 move-only 類型 - 支持
noexcept
move - 避免拷貝構造限制
- 可以零堆分配(small buffer optimization)
小結
方法 | 優點 | 缺點 |
---|---|---|
std::function | 通用、簡潔 | 不支持 move-only 捕獲 |
shared_ptr | 兼容 std::function | 堆分配、性能差、共享所有權 |
folly::MoveWrapper | 可變通使用 | 破壞復制語義、易出錯 |
自定義輕量函數包裝器(如 Folly) | 真正解決 move-only 問題 | 實現復雜,不是標準庫 |
為什么需要一種不同的 Function Wrapper
std::function
的核心問題:
要求所有可調用對象(callables)是可拷貝的(copyable)
這對很多實際用例來說,是一種不必要的限制,尤其是:
我們并不需要拷貝這些可調用對象
- 比如在線程池中提交任務時:
- 只會執行一次(one-shot)
- 不需要拷貝(只需要 move 進去,然后調用)
- 可調用對象中經常有
unique_ptr
、Promise
這類 move-only 類型
結果是:
你不能直接使用 lambda 捕獲 move-only 對象:
MoveOnlyType x;
executor.add([x = std::move(x)]() mutable { x.doStuff(); }); // std::function 不接受
- 捕獲了
MoveOnlyType
,lambda 本身就變成 move-only - 而
std::function<void()>
要求構造函數參數是 copyable
所以你真正想要的是:
一個輕量級的 function wrapper,可以 wrap:
- move-only 的 lambda
- 只 move、不 copy
- 小對象無需堆分配
- 只執行一次(one-shot callable)也沒問題
Facebook 的 Folly 庫就因此創造了:
folly::Function
:是 move-only 的 function wrapper- 用于線程池、異步任務、promise 等現代用例
- 避免了不必要的性能開銷
小結:
標準庫 std::function | 實際開發常見需求 |
---|---|
需要可復制 callable | 只需要 move、執行一次 |
可能導致堆分配 | 想要 small buffer 或 zero allocation |
不支持 move-only 捕獲 | 現代 C++ 任務常用 move-only 對象 |
如果你要構建一個支持 MoveOnly 的任務系統,那使用 std::function 是不合適的。你應該考慮: |
- 自己實現一個簡易的
MoveFunction
- 或者使用 Folly 的
folly::Function
關于 const
正確性(Const Correctness)在 std::function
中的一個重要細節
你可能會以為:
std::function<void()> f;
void someFunction(const std::function<void()>& f) {f(); // f 是 const,調用 f() 應該不會修改內部狀態
}
表面上看,f()
是 const
的成員函數,所以你以為:
- 包裝的 lambda 或函數對象也應該是 const 調用
- 內部狀態不會被修改
實際上:
R operator()(Args...) const;
這個 operator()
是 const
沒錯,但是:
- 它內部調用的是
(*callable)(args...)
,這個 callable 是void*
轉型來的 - 沒有檢查實際 wrapped 對象的 constness!
所以問題來了:
std::function<void()> f = [x = 0]() mutable { /* 修改 x */ };
f(); // 可以正常調用,雖然是 const 對象
即使 f
是 const
,只要里面包裝的是 mutable lambda
或非 const operator()
的對象:
仍然可以修改狀態!
總結就是:
看起來 | 實際上 |
---|---|
operator() 是 const | 但 wrapped callable 可能是非-const 的 |
你以為不改狀態 | 實際上可以修改狀態(比如 mutable lambda) |
所以這段話的重點理解是:
std::function::operator()
是const
,但它沒有真正地保證 const-correctness。
你不能依賴它來保證你的代碼不會修改內部狀態。
你這段內容是講解 folly::Function
的設計目的和內部實現機制,下面是逐點的理解與總結:
folly::Function
的設計動機與特點
1. Non-copyable(不可拷貝)
- 為什么?
要支持捕獲unique_ptr
、Promise
等move-only 的類型 std::function
的問題: 只支持可拷貝類型folly::Function
的解決方案:
自己就是不可拷貝的(copy constructor & copy assignment 被刪除)
folly::Function
使用 值語義(value semantics),但通過move
實現轉移所有權
2. noexcept-Movable
- 如果你定義了一個類型含有
folly::Function
成員變量,
STL 要求你這個類型如果想noexcept move
,那成員也必須是noexcept move
- 所以
folly::Function
本身實現了noexcept move
,以便:
支持std::move_if_noexcept
與 STL 容器、線程池等類型協作良好
3. Const Correct
folly::Function
有兩個版本:folly::Function<void()> // 非 const operator() folly::Function<void() const> // const operator()
- 保證 const-correctness,解決
std::function
的歷史問題 - 如果你用的是:
那里面包的 callable 也必須支持 const 調用!const folly::Function<void()>& f; f(); // 調用的是 const operator()
4. 實現細節(Implementation Details)
特性 | 說明 |
---|---|
大小 | 64 bytes(x86_64 架構) |
調用指針 | 1 個指針:真正的調用函數 |
管理函數指針 | 用于 move、destroy 等操作 |
小對象優化 | 提供 48 字節的 inline 存儲空間 |
大對象策略 | 不滿足 noexcept-movable 的對象將 heap 分配 |
哪些對象不能 inline 存儲?
- 非
noexcept
movable 的對象
會退化為堆分配(使用指針 + heap 管理)
設計目標小結:
目標 | 實現方式 |
---|---|
支持 move-only 對象 | 類型本身不可拷貝 |
高性能 | 小對象優化,避免堆分配 |
STL 容器兼容性 | noexcept move |
保證 const 正確性 | 兩個簽名:void() vs void() const |
這部分內容總結了 folly::Function
相比 std::function
的優勢、遷移實踐、適用場景以及性能表現。下面是對這些內容的逐條解釋與理解:
Trivia:互操作性
std::function
可以轉換為folly::Function
(拷貝構造)folly::Function
不能轉換為std::function
,因為std::function
需要拷貝構造,而folly::Function
是不可拷貝的
遷移到 folly::Function
大多數情況下可作為 drop-in 替代品
但也有例外:
- 如果代碼 依賴拷貝語義(很少見)
- 如果代碼 依賴不正確的 const 使用(那是個 bug,
folly::Function
會強制你修)
使用方式差異:
std::function
常常以const&
傳參
folly::Function
不可拷貝,必須以&
或&&
傳參
Facebook 內部采用
- 在
folly::Future
中替換了std::function
帶來最大受益:可使用 move-only 的回調 - 在
folly::Executor
中替換std::function
需要修改很多子類,但修改過程常揭示原有代碼問題
何時使用 std::function vs folly::Function
使用場景 | 推薦 |
---|---|
需要復制 callable 的 API | std::function |
一般回調/異步任務/只需移動語義 | folly::Function |
使用 MoveWrapper 等拷貝模擬方式 | 不要這樣做,使用 folly::Function 替代更安全清晰 |
Benchmark(性能)
調用方式 | 時間(越小越好) | 每秒調用次數(越高越好) |
---|---|---|
函數指針調用 (fn_ptr ) | ~1.3 ns | ~761M 次 |
std::function 調用 | ~2.28 ns | ~437M 次 |
folly::Function 調用 | ~1.96 ns | ~510M 次 |
std::function 創建 + 調用 | ~3.04 ns | ~329M 次 |
folly::Function 創建 + 調用 | ~2.79 ns | ~359M 次 |
folly::Function 通常快于 std::function |
總結
點 | 內容 |
---|---|
目標 | 替代 std::function ,支持 move-only 類型 |
不支持 | 拷貝(故不能做 const& 參數) |
遷移建議 | 改成 & 或 && 傳參 |
效果 | 避免 ugly workarounds(如 MoveWrapper 、shared_ptr 等) |
性能 | 與 std::function 持平或更快 |
應用 | 在 Facebook 內部已廣泛部署 |