6 ms·
Lambdas: From C++11 to C++20, Part 2
- ktpsns 8y agoWow, as somebody who has "seriously" worked with C++ professionally since ~5years and with more "user friendly" languages since decades, these new features don't blow me out of the socks. Conversely, I'm terrified by the super complex syntax of these examples. Sure, these are synthetical border cases, but it shows the abysses of C++ logic. I get headache already with error messages from contemporary C++ templating and don't even got my hands touched with error messages from future templated lambdas à la C++...
- shereadsthenews 8y agoSome of them are complex, some are less complex than they used to be. For example look at the first article in this series that discusses the very common mistake of incorrect parameter types leading to unwanted copies, which is solved by generic lambdas where the compiler is permitted to deduce the type. 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'; } );
- deleted 8y ago[deleted]
- mehrdadn 8y agoIn all honesty in my experience both of these approaches are wrong. Rather, it's using the good ol' typedef, which you should already be using pretty liberally anyway: 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.
- deleted 8y ago[deleted]
- kllrnohj 8y agoWhat new syntax? And what specifically is "super complex syntax" in these examples? Are you referring to the C++20 changes with those statements, or are you still stuck on C++11's changes? And a few of the examples are about how template errors will become less complex not more.
- danbolt 8y agoAt someone who professionally works with relatively modern C++ (eg: C++14/17) and originally learned C++98, it's not as difficult once you get into the headspace of it. 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.
- notacoward 8y ago> it's not as difficult once you get into the headspace of it. 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++.
- danbolt 8y agoI agree that the act of submerging oneself into C++ is really a frustration about the language. It's a lot like using APL or Brainfuck as you mention and getting into gear. Still, I wouldn't say that C++ is a "true high-level language" or tries to be based on the reasons you mention. It's purpose is wide-ranging, but being closer to the metal than something like Ruby, C#, or Julia. And regardless, I think there are a lot of contexts where C++'s features can give productivity benefits over the time spent working through the hard spots. That's going to be pretty subjective though. 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.
- 8y ago
- maxxxxx 8y agoIt seems to me that C++ has always been a language where you really need to understand each statement you use and its implications. Otherwise you can quickly get in trouble. 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.
- 0815test 8y agoYes, exactly. And this is why people who say that Rust is too difficult and complicated to ever get traction have no idea what "difficult and complicated" even means in this part of our industry. The whole point of projects like Rust is to make these things a lot simpler and easier to get correct, and thus to empower everyone to write better code.
- tlb 8y agoRelated to the capture of this and exceeding its lifetime, I've been doing it thusly: 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.
- eklitzke 8y agoClever.
- jdsully 8y agoI've used this in the past, but I've had trouble with "phantom callbacks". Where the logic of the program assumes the object is gone, and it ends up changing program state unexpectedly. The problem with shared pointers is ownership is not clear - just because you don't segfault doesn't mean there's no bug.
- zelos 8y agoExactly. I've had endless arguments about that exact point with shared_ptr. IMHO, shared_ptr should be used as a last resort, where ownership of something truly is shared. Use unique_ptr for everything else. Just using it for convenience for things like callbacks will lead to weird, hard-to-debug bugs in more complex code.
- tsbinz 8y agoA possible problem with this is that now the callback keeps this alive. A possible solution (in some situations) is to use a weak pointer and check if the object is still alive before using the callback, but there's some additional work to make this work nicely ...
- quietbritishjim 8y agoIn some situations, that is indeed a problem. In other situations, that is exactly what you want. For 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.
- ufo 8y agoThe website seems to have been hugged to death. Does anyone have an alternative link or cache? (I tried to get one from google but it is 404-ing)
- dmix 8y agoIts back up now but here’s a cache http://archive.is/lP77x http://archive.is/lP77x
- 0db532a0 8y agoPart 1 says that it is not correct to call a lambda which value-captures a member of a temporary instance of a class. Is that really correct? Isn’t the whole point of value-capturing that the value is copied and preserved regardless of the life of the source of the captured value?
- Negitivefrags 8y agoThis is only related to the use of the this pointer. 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.
- 0db532a0 8y agoI see from the below link that the ‘STAR this’ pointer would be captured implicitly by reference in this case. This happens regardless of a default capture type if it does exist. 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_capture https://en.cppreference.com/w/cpp/language/lambda#Lambda_cap...
- chris_wot 8y agoIsn’t it better to learn lisp first?
- amelius 8y agoIs it possible yet to use C++ productively without the preprocessor at all (including in the standard libraries)? If not, then the C++ committee should focus on that, imho.
- arijun 8y agoWhat’s the problem with the preprocessor?
- amelius 8y agoSorry, perhaps I should have elaborated on that. See for example the top answers here for a good discussion: https://stackoverflow.com/questions/14041453/why-are-preprocessor-macros-evil-and-what-are-the-alternatives https://stackoverflow.com/questions/14041453/why-are-preproc...
- kllrnohj 8y agoHerb Sutter's metaclasses proposal is basically that - the feature to replace macros. https://herbsutter.com/2017/07/26/metaclasses-thoughts-on-generative-c/ https://herbsutter.com/2017/07/26/metaclasses-thoughts-on-ge...
- fefe23 8y agoThank you! 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.