[英]C++ Lambdas, Capturing, Smart Ptrs, and the Stack: Why Does this Work?
[英]Why does capturing stateless lambdas sometimes results in increased size?
給定一個 lambda 鏈,其中每個都通過值捕獲前一個:
auto l1 = [](int a, int b) { std::cout << a << ' ' << b << '\n'; };
auto l2 = [=](int a, int b) { std::cout << a << '-' << b << '\n'; l1(a, b); };
auto l3 = [=](int a, int b) { std::cout << a << '#' << b << '\n'; l2(a, b); };
auto l4 = [=](int a, int b) { std::cout << a << '%' << b << '\n'; l3(a, b); };
std::cout << sizeof(l4);
我們可以觀察到,得到的l4
的sizeof
等於1
。
這對我來說很有意義。 我們按值捕獲 lambda,每個對象的sizeof
必須等於1
,但由於它們是無狀態的,因此適用類似於[[no_unique_address]]
的優化(特別是因為它們都有唯一的類型)。
但是,當我嘗試為鏈接比較器創建通用構建器時,不再發生這種優化:
template <typename Comparator>
auto comparing_by(Comparator&& comparator) {
return comparator;
}
template <typename Comparator, typename... Comparators>
auto comparing_by(Comparator&& comparator, Comparators&&... remaining_comparators) {
return [=](auto left, auto right) {
auto const less = comparator(left, right);
auto const greater = comparator(right, left);
if (!less && !greater) {
return comparing_by(remaining_comparators...)(left, right);
}
return less;
};
}
struct triple {
int x, y, z;
};
auto main() -> int {
auto by_x = [](triple left, triple right) { return left.x < right.x; };
auto by_y = [](triple left, triple right) { return left.y < right.y; };
auto by_z = [](triple left, triple right) { return left.z < right.z; };
auto comparator = comparing_by(by_x, by_z, by_y);
std::cout << sizeof(comparator);
}
注 1:我知道 compare_by 效率低下,有時以冗余方式調用comparing_by
器。
為什么在上述情況下, comparator
的結果sizeof
等於3
而不是1
? 畢竟,它仍然是無國籍的。 我哪里錯了? 或者它只是所有三大編譯器中錯過的優化?
注2:這純粹是一個學術問題。 我不是想解決任何特定的問題。
第一個例子中發生的事情不是你想象的那樣。 假設l1
具有類型L1
、 l2
L2
等。這些是這些類型的成員:
struct L1 {
// empty;
};
sizeof(L1) == 1
struct L2 {
L1 l1;
};
sizeof(L2) == sizeof(L1) // 1
struct L3 {
L2 l2;
};
sizeof(L3) == sizeof(L2) // 1
struct L4 {
L3 l3;
};
sizeof(L4) == sizeof(L3) // 1
在您的下一個示例中,您按值捕獲所有 lambda,因此閉包類型具有三個不重疊的成員,因此大小至少為 3。
[[no_unique_address]]
通常不能應用於閉包類型的數據成員(考慮一個空的 class 將其地址放在全局映射中)。
編譯器可以對“行為良好的類型”(可能是可簡單復制的空類型?)使用空基優化,因此這可能是一個錯過的優化。 該標准說明了可以做什么([expr.prim.lambda.closure]p2):
閉包類型不是聚合類型。 一個實現可以定義不同於下面描述的閉包類型,前提是這不會改變程序的可觀察行為,除非改變:
- 閉合類型的尺寸和/或 alignment,
- 閉包類型是否可以簡單復制([class.prop]),或者
- 閉包類型是否為標准布局 class ([class.prop])。
所以大小的變化是可以的,但必須這樣做,以便is_empty_v<lambda_that_captures_stateless_lambda>
不是true
(因為這是一個可觀察的行為)
要“手動”應用此優化,您可以不調用 lambda comparator(left, right)
,而是默認構造閉包類型的類型並調用它( decltype(comparator){}(left, right)
)。 我在這里實現了: https://godbolt.org/z/73M1Gd3o5
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.