
Under the hood of C++ lambdas and std::function - ingve
http://shaharmike.com/cpp/lambdas-and-functions/
======
doomrobo
If anyone is curious, as of C++14 you can `move` variables into a lambda scope
(like Rust's move ||) as follows

    
    
        Foo foo; // Foo implements a move constructor
        auto l = [cap = std::move(foo)](){ do_stuff(cap);} // Make the lambda
        do_stuff(foo); // ILLEGAL. Cannot use moved value
        l(); // Run the lambda

~~~
rjeli
At this time, what can Rust accomplish with the borrow checker that C++ can't
accomplish with move semantics?

~~~
bjz_
Rust's moves are implicit, making it easier to write code that doesn't copy
heap allocated memory, and you can't get 'use after moves' like in C++ - the
type system will pick that up.

~~~
rjeli
Oh, having only read about and not used recent C++ practices, I assumed use-
after-move would be caught at compile time. Looking around a bit, it seems
there's not even a warning for this; is there a reason? It seems like it would
be very useful.

~~~
catnaroek
In C++, "moving" and "copying" are a matter of class design. While you are
expected to use move (resp. copy) constructors and assignments to, well, move
(resp. copy); the language doesn't rule out using them for other purposes.

In Rust, assignment and argument passing always move, "moving" always does the
right thing (make a shallow copy and invalidate the original object), and you
can't override this behavior. Furthermore, Rust splits what C++ calls
"copying" into two concepts: shallow copying (which is the same as moving,
except the original object isn't invalidated, and the type checker guarantees
you can only do it when it makes sense) and deep cloning (which may be
expensive and needs to be explicitly requested by the programmer).

This is why Rust can track which objects are no longer usable.

------
hellofunk
>Lambdas are also awesome when it comes to performance. Because they are
objects rather than pointers they can be inlined very easily by the compiler,
much like functors. This means that calling a lambda many times (such as with
std::sort or std::copy_if) is much better than using a global function. This
is one example of where C++ is actually faster than C.

That's insight I've never seen anywhere before.

~~~
sebastic
Also C++ is generally as fast as C. If anything, it's faster thanks to
increased opportunities for inlining.

Whoever says C++ is slower than C is misinformed and/or overgeneralizing.

Professional C++ programmers know this already though.

~~~
hellofunk
Well, much of C++ involves virtual function calls, and those are indeed slower
than non-virtual calls. C doesn't have virtual calls, so that is perhaps one
reason why C is generally assumed to be faster. That doesn't mean you must use
virtual functions in C++; in fact, I don't use inheritance in my own code
whatsoever, therefore I avoid the virtual calls but more importantly the
restrictions that can creep into a tight OO hierarchy.

And std::function, from what I understand, is generally not as fast as a
function pointer, a stored lambda, a function object, since it must do some
run-time checks to see how to call what it is storing.

~~~
mike_hock
The only virtual call that you _necessarily_ get with polymorphism is the
destructor call.

Whenever you _need_ to make a function virtual in C++, you'd need to store a
function pointer in an equivalent C design, somehow.

~~~
hellofunk
Are you suggesting that C++ virtual functions have the same overhead as a C
function pointer? I've never heard that; with the virtual call, there is the
lookup + the invocation; perhaps a C app that stores function pointers in a
hash map also has this sort of lookup, is that what you mean?

~~~
mikeash
Vtable lookup in a typical C++ implementation is just loading a fixed offset
in an array. In C terms, it's basically doing object->vtable[42]. In C code
with function pointers, you're typically performing that exact same operation.
You can go slightly faster if you store the function pointer in the object
directly, at the cost of increased memory consumption per object if you have
more than one function pointer.

------
thelema314
I kind of wish this article had gotten into some details of how various
compilers implement std::function; while it was nice to see some details of
clang++'s 32-byte size of all std::function to remove the need for dynamic
memory for many function objects, I'm still wishing I had more details on
efficiency/cost of using std::function vs. plain lambdas or function pointers
or other solutions.

------
Animats
From the article: _" My favorite expression in C++ is [](){}();, which
declares an empty lambda and immediately executes it."_

Sigh.

~~~
Too
In JavaScript it's quite common to see self executing lambdas like this (not
empty though), because there variables have function scope rather than block
scope so it's a way to limit the scope and capture closures. Does it have any
practical use in c++?

~~~
catnaroek
No, you don't need this in C++. You can use a normal (brace-delimited) block
for this purpose.

~~~
evincarofautumn
The key difference being that a lambda is an expression, while a block is not,
unless you use GCC’s statement-expression syntax of “({…})”.

~~~
catnaroek
Oh, I forgot that, you're absolutely right. I'm getting too used to languages
where everything is an expression. :-)

