gpt4 book ai didi

c++ - 为什么捕获无状态 lambda 有时会导致大小增加?

转载 作者:行者123 更新时间:2023-12-05 02:30:02 24 4
gpt4 key购买 nike

给定一个 lambda 链,其中每个 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);

我们可以观察到,生成的 sizeof of l4 is equal to 1 .

这对我来说很有意义。我们按值捕获 lambda,每个对象的 sizeof 都必须等于 1,但由于它们是无状态的,因此优化类似于 [[no_unique_address] ] 一个适用(特别是因为它们都有独特的类型)。

但是,当我尝试创建用于链接比较器的通用构建器时,this optimization no longer takes place :

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:我知道 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]]不能普遍应用于闭包类型的数据成员(考虑将其地址放在全局映射中的空类)。

编译器可以对“行为良好的类型”(可能是一个 trivilly-copyable 空类型?)使用空基优化,所以这可能是一个错过的优化。该标准说明了可以做什么 ([expr.prim.lambda.closure]p2):

The closure type is not an aggregate type.An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

  • the size and/or alignment of the closure type,
  • whether the closure type is trivially copyable ([class.prop]), or
  • whether the closure type is a standard-layout 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

关于c++ - 为什么捕获无状态 lambda 有时会导致大小增加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71965085/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com