Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If lambdas don't have a specified type, how does std::function accept a lambda?

Tags:

c++

c++11

Can anyone explain how lambda functions are represented in std::function? Is there an implicit conversion by the compiler and std::function used as a container?

I recently asked a slightly different question, which was marked as a duplicate of this question. The answer is the type of a lambda function is not defined and unspecified. I have found some code that appears to provide a container for the lambda function as follows below. I have also included a Stroustrup quote, which seems to contradict that lambda functions do not have a type defined, saying however it is a function closure type. This is only confusing the matter further.

Update: Partial answer regarding implementation of function<> is here.

Appreciate your guidance.

#include <iostream>
#include <vector>
#include <functional>
using namespace std;

static vector<function<void(int)>> cbl;
static vector<function<int(int)>> cblr;

class GT {
public:
    int operator()(int x) {return x;}
};

void f()
{
    auto op = [](int x) {cout << x << endl;}; 
    cbl.push_back(op);

    for (auto& p : cbl)
        p(1);

    auto op2 = [](int x) {return x;};
    cblr.push_back(op2);
    cblr.push_back(GT());
    for (auto& p : cblr)
        cout << p(99) << endl;
}

int main(int argc, char *argv[])
{
    f();
    return 0;
}

Compilation and result:

g++ -pedantic -Wall test130.cc && ./a.out
1
99
99

Stroustrup talks about this in C++ 4th Ed Page 297:

To allow for optimized versions of lambda expressions, the type of a lambda expression is not defined. However, it is defined to be the type of a function object in the style presented in §11.4.1. This type, called the closure type, is unique to the lambda, so no two lambdas have the same type. Had two lambdas had the same type, the template instantiation mechanism might have gotten con- fused. A lambda is of a local class type with a constructor and a const member function operator()().

like image 410
notaorb Avatar asked Jun 11 '20 19:06

notaorb


People also ask

Can std :: function store lambda?

Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

Is a lambda a std :: function?

Lambda's type One important thing to note is that a lambda is not a std::function .

How do lambda functions work C++?

A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.

Which is a valid type for this lambda function?

A lambda expression is a function or subroutine without a name that can be used wherever a delegate is valid. Lambda expressions can be functions or subroutines and can be single-line or multi-line. You can pass values from the current scope to a lambda expression. The RemoveHandler statement is an exception.


1 Answers

The type is there. It’s just that you don’t know in advance what it is. Lambdas have type - just the standard says nothing about what that type is; it only gives the contracts that type has to fulfill. It’s up to the compiler implementers to decide what that type really is. And they don’t have to tell you. It’s not useful to know.

So you can deal with it just like you would deal with storage of any “generic” type. Namely: provide suitably aligned storage, then use placement new to copy-construct or move-construct the object in that storage. None of it is specific to std::function. If your job was to write a class that can store an arbitrary type, you’d do just that. And it’s what std::function has to do as well.

std::function implementers usually employ the small-object optimization. The class leaves some unused room in its body. If the object to be stored is of an alignment for which the empty room is suitable, and if it will fit in that unused room, then the storage will come from within the std::function object itself. Otherwise, it’ll have to dynamically allocate the memory for it. That means that e.g. capture of intrinsic vector types (AVX, Neon, etc) - if such is possible - will usually make a lambda unfit for small object optimization storage within std::function.

I'm making no claims as to whether or if the capture of intrinsic vector types is allowed, fruitful, sensible, or even possible. It's sometimes a minefield of corner cases and subtle platform bugs. I don't suggest anyone go and do it without full understanding of what's going on, and the ability to audit the resulting assembly as/when needed (implication: under pressure, typically at 4AM on the demo day, with the suits about to wake up soon - and already irked that they have to interrupt their golf play so early in the day just to watch the presenter sweat).

like image 126
Kuba hasn't forgotten Monica Avatar answered Nov 15 '22 05:11

Kuba hasn't forgotten Monica