Under the hood of C++ lambdas and std::function
shaharmike.com
shaharmike.com
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 lambdaActually that depends on the implementation of "Foo". After all "foo" has not been destroyed yet.
Edit: An instance of std::unique_ptr for example points to nullptr after having been moved to another instance (C++14 Standard §20.8.1p4.2).
Right now the only way to enforce this is by convention: 1. move/unique/shared_ptr=receiver allowed to keep pointer, 2. raw ptr=totally banned, 3. reference=caller must outlive callee and called function is not allowed to keep pointer after returning. The last scenario here is not enforcable by the compiler.
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.
I've referred to the attempts to fix C++ ownership semantics with templates as "wallpapering over the mold".
Yeah, basically that it's quite difficult to implement especially with C++'s baggage and undefined behaviour, from what I remember
MovableObject a, b, c;
b = std::move(a);
c = std::move(a);
`c` may contain garbage.BTW the "move trait" (as its called in rust) is a related but orthogonal concept to its borrow checker.
That's insight I've never seen anywhere before.
The "C++ with lambda is faster than C with function" comes from C's qsort function that takes a function pointer to a comparator and is not inlined by the compiler. This can result in it being slower than std::sort.
BTW since lambdas without capture decay to function pointers, you can pass such a lambda to qsort.
This is a matter of compilation units, not C vs. C++. If you put qsort() in a header file, and call it with a function pointer in the same compilation unit, it will get inlined and work exactly the same way as std::sort (which is in a header file since it's a template).
You can pretty easily verify this yourself by doing a C++ template and a C function with function pointers (in a header file!), compile and inspect the resulting assembly code.
Link-time optimization may relax the compilation unit requirement, but it's not very widely used yet.
Because of compile-time polymorphism, you can pass in prvalue function pointers and objects alike and the compiler will be able to inline.
C doesn't have templates, therefore it doesn't have compile-time polymorphism, therefore it's much harder for the compiler to inline.
Compilers like GCC and Clang can inline in some runtime-polymorphism circumstances. See -flto and -fdevirtualize.
A C compiler can do the same inlining and optimizing as long as the called function (e.g. qsort) and the function that's pointed-to (e.g. the comparison function) are in the same compilation unit.
C++ templates do not add any magic here, std::sort() is simply faster because it's in a header file and qsort() is not. If you pass a function pointer to a function in a different object file to std::sort, it won't have a lot of advantages over qsort in terms of optimizations/performance (it still does know sizeof(T), though).
Link-time optimization should make standard qsort-type functions perform similarly to std::sort.
Whoever says C++ is slower than C is misinformed and/or overgeneralizing.
Professional C++ programmers know this already though.
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.
Whenever you need to make a function virtual in C++, you'd need to store a function pointer in an equivalent C design, somehow.
A generic collection is usually faster when implemented using templates instead of void*.
Sigh.
template <typename... T>
void foo(T......); void foo() {
// initialized once when foo is first called
static auto lazy_data = ...;
// use lazy_data...
}
As of c++11, the compiler is required to make this threadsafe (initializer only runs once, even if foo() is called concurrently), typically by inserting locks.Since this only applies to the initializer, for complex initialization a self-executing lambda can be used:
void foo() {
static auto lazy_data = []{
auto data = new Whatever();
// initialize data...
return data;
}();
// use lazy_data...
}That includes all the JS pass-a-lambda-as-a-callback shenanigans.
[[]][[]][]()[[]][[]]{}();