繁体   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