Lambdas: From C++11 to C++20, Part 2
bfilipek.com
bfilipek.com
class Foo : enabled_shared_from_this<Foo> {
void start_callback() {
auto thisp = shared_from_this();
function_taking_callback([this, thisp](int err) {
if (err) errFlag = true;
});
}
bool errFlag{false};
}
The idea is that thisp is captured in the lambda and extends the lifetime of this until the callback is destroyed. And because this is also captured you can refer to member variables implicitly.Just using it for convenience for things like callbacks will lead to weird, hard-to-debug bugs in more complex code.
[thisp = shared_from_this()]
would save a potentially expensive atomic increment & decrement which happens when copying the shared_ptrFor example, say you are passing a message along a sequence of threads (e.g. read from database in one thread, CPU-bound processing in the next, write back to database in the third). In that case, you probably want the lifetime of the request object to exactly match the time where there is an outstanding callback alive (either being called it waiting in a thread's queue).
The ownership model in that case is very clear: threads own queues which own callbacks which own shared pointers which own message handling objects.
std::map<std::string, int> numbers {
{ "one", 1 }, {"two", 2 }, { "three", 3 }
};
// each time entry is copied from pair<const string, int>!
std::for_each(std::begin(numbers), std::end(numbers),
[](const std::pair<std::string, int>& entry) {
std::cout << entry.first << " = " << entry.second << '\n';
}
);
This has an error, which is harder to commit in c++14 and forward, with a more compact expression: std::for_each(std::begin(numbers), std::end(numbers),
[](const auto& entry) {
std::cout << entry.first << " = " << entry.second << '\n';
}
); typedef std::map<std::string, int> Numbers;
Numbers numbers {
{ "one", 1 }, {"two", 2 }, { "three", 3 }
};
// each time entry is copied from pair<const string, int>!
std::for_each(std::begin(numbers), std::end(numbers),
[](const Numbers::value_type& entry) {
std::cout << entry.first << " = " << entry.second << '\n';
}
);
Or if you're feeling particularly pedantically correct, you can replace const Numbers::value_type& with Numbers::const_reference, but I don't go that far generally.IMHO one of the mistakes of (many, even very experienced) C++ programmers is that they don't realize typedefs are such a powerful tool to be used liberally throughout a code base, and they instead overuse auto and decltype. Typedefs prevent you from repeating yourself, prevent accidents like these, make changing the underlying types a breeze, are self-documenting, and (when needed on occasion) let you use a different type for a variable than the expression used to initialize it, which isn't possible with auto.
I'd definitely prefer writing in OCaml every day if I could, but I can understand why a lot of C++ is the way it is, and having the newer features is an improvement on the day-to-day.
The same could be said of APL, PHP, or Brainfuck. Having to get into the headspace of C++ is one of the language's weaknesses. A true high-level language doesn't force programmers to spend half their time thinking about how the compiler internals deal with lvalues vs. rvalues, move semantics, decision trees for choosing which promotion or operator overload to do, and so on ad nauseam. Compilers are supposed to be assistants, there to relieve programmers of some drudgery, but C++ compilers are always like malicious assistants determined to do the opposite whenever they can. It's often more work getting them to do the right thing than to do it yourself in Plain Old C - which is what I'd rather do if I can't use a better high-level language than C++.
JavaScript does this with the endless development tooling options and configurations.
So it’s not limited to the language but I see adding to the mental load of a programmer - while they are programming - to be a far worse issue. Context switching is hard enough without all that.
I feel like Rust (eg: `cargo build`) or Ruby gems provide a very different, standardized experienced compared to the two.
Based on what you mention though, I'd encourage you to use "Plain Old C" (ANSI, C99, or C11?) if it helps you be more productive. I can imagine with more and more languages fitting the that semi low-level space (eg: Rust and Zig) coming on the scene, there's a lot more opportunity to find the right tool than ever before.
I doubt you'll find many people who are would say "C++ is intended to be a true high-level language".
If that is actually how C++ is being sold that is indeed wrong. Same if that's how Rust were hypothetically being sold.
Both make you think really hard about ownership for purposes of faster memory management. They're not "true high-level lanaguages". They are systems languages with zero-cost abstractions where possible.
And a few of the examples are about how template errors will become less complex not more.
So I think C++ should either be close to a full time job where you have to learn all the details or you probably shouldn't do it.
If you have a member of a class called s then any mention of s inside a member function of the class is really a mention of this->s.
After you understand that, you can just apply all the regular rules of lambdas to understand why it gets you in to trouble.
this->s is a capture of this, not s. this is a pointer and therefore capturing it by value isn't going to extend the lifetime of the object it points to.
To truly capture a member by value, you can (1) make a copy of that value in the enclosing function, thus making it an automatic variable which can be value-captured, (2) value-capture through an initialiser in the capture list or (3) you can indicate ‘STAR this’ (since C++17) as a capture, which then captures the ‘this’ as a value copy and thus transitively your member.
https://en.cppreference.com/w/cpp/language/lambda#Lambda_cap...
If not, then the C++ committee should focus on that, imho.
https://herbsutter.com/2017/07/26/metaclasses-thoughts-on-ge...
https://stackoverflow.com/questions/14041453/why-are-preproc...
This was helpful, concise, well written, yet did not throw a SUBSCRIBE TO OUR NEWSLETTER popup in my face, did not ask me to create an account, did not bombard me with ads and did not come across as the usual LOOK AT ME I'M AWESOME HIRE ME. If I could upvote this more, I would.