CppCon 2017 學習:folly::Function A Non-copyable Alternative to std::function

你說的內容是關于 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++)
SBO32 字節內的對象放棧上,否則放堆上
可復制會復制被封裝對象
移動非 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::movestd::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_ptrPromise 等 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_ptrPromise 這類 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 對象

即使 fconst,只要里面包裝的是 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_ptrPromisemove-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 的歷史問題
  • 如果你用的是:
    const folly::Function<void()>& f;
    f(); // 調用的是 const operator()
    
    那里面包的 callable 也必須支持 const 調用!

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 替代品

但也有例外:

  1. 如果代碼 依賴拷貝語義(很少見)
  2. 如果代碼 依賴不正確的 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 的 APIstd::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(如 MoveWrappershared_ptr 等)
性能std::function 持平或更快
應用在 Facebook 內部已廣泛部署

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87500.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87500.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87500.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PyTorch 中Tensor常用數據結構(int, list, numpy array等)互相轉換和實戰示例

在 PyTorch 中&#xff0c;tensor 是一種強大且靈活的數據結構&#xff0c;可以與多種 Python 常用數據結構&#xff08;如 int, list, numpy array 等&#xff09;互相轉換。下面是詳細解釋和代碼示例&#xff1a; 1. Tensor ? int / float 轉為 int / float&#xff08;前提…

計算機網絡與數據通信基礎

第一章 計算機網絡概述 1. 計算機網絡的核心概念 1.1 定義 將 地理分散 的、具有 獨立處理能力 的計算機系統&#xff08;主機/Host&#xff09;&#xff0c;通過 傳輸介質 與 網絡設備 互連&#xff0c;在 網絡協議 和 軟件 支持下實現 資源共享 與 數據通信 的系統。 關鍵術…

【統計術語】

文章目錄 基礎概念術語基期與現期增長量與增長率環比與同比 比重術語平均數術語特殊增長術語其他常用術語 基礎概念術語 基期與現期 基期&#xff1a;作為基礎參照的時期&#xff0c;一般指過去的時間 現期&#xff1a;與基期對比的時期&#xff0c;一般指現在的時間 示例&am…

XXE(XML外部實體注入)詳解

目錄 一、XXE漏洞簡介 二、XML詳解 (一) XML文檔結構 1. 文檔聲明 2. XML文檔類型定義&#xff08;DTD&#xff09; 3. XML文檔元素 4. XML文檔示例 三、XXE漏洞類型 四、XXE漏洞挖掘技巧 五、XXE漏洞危害 (一) 文件讀取 (二) 內網探測 1. 端口探測 2. 主機存活探…

深入解析JVM字節碼執行引擎

JVM 字節碼執行引擎。它是 JVM 核心組件之一&#xff0c;負責實際執行加載到內存中的字節碼指令。你可以將它想象成 JVM 的“CPU”。 核心職責&#xff1a; 加載待執行的字節碼&#xff1a; 從方法區&#xff08;元空間&#xff09;獲取已加載類的方法字節碼。創建和管理棧幀…

華為OD機試-MELON的難題-DFS(JAVA 2025A卷)

題意是從N快雨花石中找出最少拿出雨花石的塊數&#xff0c;使得雨花石可以均分&#xff0c;直接使用dfs解決此類組合問題 package com.example.demo.bean;import java.util.Arrays; import java.util.LinkedList; import java.util.Scanner;public class YuHuaStone {public s…

鴻蒙數據庫操作

一、使用關系型數據庫實現數據持久化&#xff0c;需要獲取一個RdbStore&#xff0c;其中包括建庫、建表、升降級等操作。 const STORE_CONFIG: relationalStore.StoreConfig {name: AnyOffice.db, // 數據庫文件名securityLevel: relationalStore.SecurityLevel.S1, // 數據庫…

基于ARM SoC的半導體測試

ARM SoC&#xff08;System on Chip&#xff09; 是一種集成了多個關鍵計算組件的單片系統芯片&#xff0c;廣泛應用于移動設備、嵌入式系統、物聯網&#xff08;IoT&#xff09;和半導體測試設備等領域。它的核心設計理念是“高度集成”&#xff0c;將處理器、內存、外設接口等…

JavaEE->多線程2

目錄 一、線程安全&#xff08;重點&#xff09; 1.線程安全演示 2.線程不安全的原因 1.線程是搶占式執行的&#xff08;執行順序是隨機的&#xff09; 2.多個線程同時修改了同一個變量 3.原子性 4.內存可見性 5.指令重排序&#xff08;有序性&#xff09; 二、解決線…

Flutter TCP通信

啟動TCP服務 Future<void> startServer() async {final server await ServerSocket.bind(InternetAddress.anyIPv4, 12345);print(Server listening on ${server.address}:${server.port});server.listen((Socket socket) {print(Client connected: ${socket.remoteAddr…

flask拆分計劃

兩個啟動鏈接&#xff0c;看日志提示是因為2次啟動&#xff0c;一次是database&#xff0c;一次是xmind2&#xff0c;去掉一次就可以&#xff0c;如何去掉一次&#xff1f; 這里啟動也調用了一次&#xff0c;所以測試環境注釋掉&#xff0c;如下圖&#xff0c;也就調用了一次

【生活】ECMO原理、作用、費用及使用方法

博客目錄 一、ECMO 是什么&#xff1f;二、ECMO 的作用1. 替代肺功能&#xff08;氧合與二氧化碳清除&#xff09;2. 替代心臟功能&#xff08;循環支持&#xff09;3. 為其他治療爭取時間4. 用于心肺復蘇&#xff08;ECPR&#xff09; 三、ECMO 的費用1. 設備使用費2. 耗材費用…

Profinet轉EtherCAT網關模塊怎么用:案例分享

在某制造工廠西門子S7-1200 PLC中&#xff0c;存在一個技術難題&#xff0c;即伺服驅動器與可編程邏輯控制器&#xff08;PLC&#xff09;之間的通訊不兼容問題。具體而言&#xff0c;PLC采用的是PROFINET通訊協議&#xff0c;而伺服EtherCAT協議驅動器則需要EtherCAT協議進行數…

什么是 NLP-NLP基礎知識體系的系統認知

NLP基礎知識體系的系統認知 一、引言 今天的學習內容集中于自然語言處理&#xff08;NLP&#xff09;的基本概念、發展歷程、核心任務及文本表示技術。通過這一學習過程&#xff0c;我對NLP這門學科有了更加系統和深入的認識&#xff0c;并且理解了NLP技術的廣泛應用及其復雜…

數據結構 學習 鏈表 2025年6月14日08點01分

單向鏈表: 線性數據結構 由一系列節點組成 每個節點包含: 數據部分:存儲實際數據 指針部分:儲存指向下一個節點的引用 特點1,每個節點只有一個指向下一個節點的指針 特點2,只能從頭到尾 單向遍歷 特點3,不需要連續的內存空間 特點4,插入和刪除效率高 特點5,隨機訪問 效率低 …

使用 Kubernetes 部署 PHP 留言板應用(含 Redis 架構)

使用 Kubernetes 部署 PHP 留言板應用&#xff08;含 Redis 架構&#xff09; 文章目錄 使用 Kubernetes 部署 PHP 留言板應用&#xff08;含 Redis 架構&#xff09;教程概述技術架構特點 準備工作環境要求 Redis 數據庫部署Redis 主從架構原理創建 Redis 領導者 Deployment部…

MATLAB提供的兩種畫誤差矩陣的函數

MATLAB在統計學和機器學習工具包中提供了兩種畫誤差矩陣&#xff08;Confusion matrix&#xff09;的函數。 figure; plotconfusion(YValidation,YPred)figure; cm confusionchart(YValidation,YPred) cm.Title Confusion Matrix for Validation Data; cm.RowSummary row-n…

【Java學習筆記】泛型

泛型 一、泛型的引出 代碼示例 public class pra {public static void main(String[] args) {ArrayList arrayList new ArrayList();arrayList.add("java");arrayList.add("jack");arrayList.add("jom");arrayList.add(new a());for (Object…

SpringMVC系列(一)(介紹,簡單應用以及路徑位置通配符)

0 引言 作者正在學習SpringMVC相關內容&#xff0c;學到了一些知識&#xff0c;希望分享給需要短時間想要了解SpringMVC的讀者朋友們&#xff0c;想用通俗的語言講述其中的知識&#xff0c;希望與諸位共勉&#xff0c;共同進步&#xff01; 1 SpringMVC介紹 SpringMVC本質上…

Java中如何使用lambda表達式分類groupby

Java中如何使用lambda表達式分類groupby Java中如何使用lambda表達式分類groupby分類問題場景傳統手寫方式lambda使用groupBy()方法一行結束&#xff01;&#xff01;&#xff01;完整代碼 Java中如何使用lambda表達式分類groupby 分類問題場景 比如一群學生根據性別和年齡排…