[英]How std::unique_ptr have no size overhead if using lambda
跟進這個問題,
std::unique_ptr 如何沒有大小開銷?
我做了以下代碼,結果出乎意料:
#include <memory>
#include <cstdio>
void f(void *p){
free(p);
}
int main(){
auto x1 = [](void *p){ free(p); };
auto x2 = [](void *p){ f(p); };
printf("%zu\n", sizeof(std::unique_ptr<int> )); // 8, expected
printf("%zu\n", sizeof(std::unique_ptr<int, decltype(&f)> )); // 16, expected
printf("%zu\n", sizeof(std::unique_ptr<int, decltype(x1)> )); // 8, unexpected
printf("%zu\n", sizeof(std::unique_ptr<int, decltype(x2)> )); // 8, unexpected
}
lambda 的最后兩種類型的大小為 8,即使它們與f()
做同樣的事情。
這是怎么做的?
對 lambda 進行脫糖並簡化decltype
,您實際上寫道:
int main(){
struct x1_impl {
void operator()(void *p) { free(p); }
};
x1_impl x1;
struct x2_impl {
void operator()(void *p) { f(p); }
};
x2_impl x2;
printf("%zu\n", sizeof(std::unique_ptr<int>));
printf("%zu\n", sizeof(std::unique_ptr<int, void (*)(void*)>));
printf("%zu\n", sizeof(std::unique_ptr<int, x1_impl>));
printf("%zu\n", sizeof(std::unique_ptr<int, x2_impl>));
}
unique_ptr<T, Del>
需要同時存儲T*
和Del
。 由於x1_impl
和x2_impl
沒有數據成員,它們不需要存儲,所以最后兩個unique_ptr
可以只存儲一個T*
。 請注意, decltype(&f)
是void (*)(void*)
,但decltype(x1)
是一個未命名的空struct
。 Lambda 和 function 指針實際上完全不同。 使用 function 指針,要執行的代碼僅在運行時才知道,位於指針后面。 使用 lambda,要執行的代碼在編譯時是已知的,而 lambda object 實際上是閉包,捕獲變量的集合。 在這里,沒有這樣的變量,所以 lambdas 不需要存儲任何東西。
無捕獲的 lambda 不需要任何子對象; 它只是一種具有operator()
重載的類型。 因此,它可以是(但不是必須是)空類型。 unqiue_ptr
被允許(但不是必需的)優化它“包含”刪除器類型的方式,這樣,如果刪除器類型是空的 class 類型,那么它可以使用各種技術來確保該類型不占用存儲空間unique_ptr
實例本身。
有幾種方法可以做到這一點。 unique_ptr
可以從類型繼承,依靠 EBO 優化掉基礎 class。 使用 C++20,它可以將其作為成員子對象,依靠[[no_unique_address]]
屬性來提供空成員優化。 無論哪種情況, unique_ptr<T>
需要的唯一實際存儲是指向T
的指針。
相比之下,function 指針是 function 指針。 它是必須具有存儲的基本類型,因為它可以指向具有該簽名的任何 function。 一個類型本質上包含成員 function 作為類型本身的一部分來調用; function 指針沒有。 該類型的實例實際上並不需要存儲來找到它的operator()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.