簡體   English   中英

為什么捕獲無狀態 lambda 有時會導致大小增加?

[英]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);

我們可以觀察到,得到l4sizeof等於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具有類型L1l2 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM