2

Given the following function that returns a lambda

auto foo(int y) {
    return [=](int x) { return x + y; };
}

GCC and Clang produce the following assembly

foo(int):
    mov     eax, edi
    ret

which as far as I can tell is equivalent to this

int bar(int n) {
    return n;
}

Yet bar seems to function as expected.

auto fn = foo(2);
std::cout << fn(3); //prints 5
Chris_F
  • 4,991
  • 5
  • 33
  • 63
  • The [as-if](https://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule) rule allows comiler to do anything as long as the observed behavior is the same. Your `foo` returns a lambda so `fn` is a lambda. – Jason Jan 01 '23 at 03:46
  • @JasonLiam how is the number 2 equivalent to a closure that takes an integer and returns and integer? – Chris_F Jan 01 '23 at 03:48
  • If you look at the full optimized assembler, that consists of `mov esi, 5` you see `foo` is not called. The compiler drops it off, just keeps the symbol that does nothing, – 273K Jan 01 '23 at 03:54
  • Even with -O0 `foo` appears to be a no-op. – Chris_F Jan 01 '23 at 03:56
  • How it's no-op with -O0? `foo` allocates an object on stack and keeps the argument in the object. – 273K Jan 01 '23 at 03:59

1 Answers1

3

Remember that a closure type is a class type. Your function is similar to this:

auto foo(int y) {
    // return [=](int x) { return x + y; };
    struct closure_type {
        int y;
        auto operator()(int x) const { return x + y; }
    };
    return closure_type{ y };
}

And, once the types are removed, there really is no difference between returning an int and returning a trivial struct with a single int data member.

You would have the exact same experience with your int returning function if you changed the types:

using closure_type = decltype(foo(int{}));
auto fn = std::bit_cast<closure_type>(bar(2));
std::cout << fn(3); //prints 5
Artyer
  • 31,034
  • 3
  • 47
  • 75