4

I see a lot of questions in which generalized lambda captures are used for various things, but nothing that explains exactly what they are or why they were added to the standard.

I've read what appears to be the document describing the updated to the standard necessary for generalized lambda captures to exist, but it doesn't really say why they were created, nor does it really give a good summary of exactly how they work. It's mostly just a bunch of dry 'strike this out here and add this language here' stuff.

So, what are they? Why would I want to use one? What rules do they follow? For example, it seems to allow capture expressions. When are these expressions evaluated? If they result in side effects, when do those take effect? If several expressions are evaluated, is there a guaranteed evaluation order?

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • as I understand it (I might be wrong) generalized lambda capture is strictly to make it possible to pass rvalue references to the lambda with preserving its rvalue reference properties (rvalue reference type), not sure though if it covers something more that wasn't already available in C++11 – W.F. Jan 07 '17 at 08:46
  • @Peter - Yes, more or less. They are complex beasts, but I have used them, and basically understand how they work. And example where I used lambda: https://bitbucket.org/omnifarious/sparkles/src/0ce19e3f505642ea7a2ca8341388b95848467a85/work_queue_test.cpp?at=default&fileviewer=file-view-default#work_queue_test.cpp-86 – Omnifarious Jan 07 '17 at 08:49
  • https://en.wikipedia.org/wiki/C++14#Lambda_capture_expressions – cpplearner Jan 07 '17 at 09:25
  • Would those who like to close this question please suggest in a comment how it could be improved? – Walter Jan 07 '17 at 10:40
  • 2
    For pretty much every pure-wording paper for a new feature, there's a (usually earlier) paper with a description of the design. In this case, it's [N3610](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html). – T.C. Jan 07 '17 at 14:29

1 Answers1

7

As usual, Scott Meyers offers a great explanation of what they are, why you would want to use them, and roughly what rules they follow, in Effective Modern C++ Item 32: Use init capture to move objects into closures. Here is a short summary:

Since C++11 only has by-value and by-reference capture, by-move capture was missing. Instead of adding it, C++14 introduced the generalized lambda capture, also known as init capture. It allows you to specify

  1. the name of a data member in the closure class generated from the lambda, and
  2. an expression initializing that data member.

With this, an init capture is a new and more general capture mechanism, which covers the three above by-* captures and more (but no default capture mode).

Coming back to the main motivation of by-move capture, it can be implemented by an init capture as follows:

auto pw = std::make_unique<Widget>(); 

// configure *pw

auto func = [pWidget = std::move(pw)] { 
    return pWidget->isValidated() && pWidget->isArchived(); 
};

You can simulate an init capture in C++11 with

  • a hand-written class:

    class IsValAndArch {
    public:
        using DataType = std::unique_ptr<Widget>;
    
        explicit IsValAndArch(DataType&& ptr) : pw(std::move(ptr)) {}  
        bool operator()() const { return pw->isValid() && pw->isArchived(); }    
    
    private:
        DataType pw;
    };
    
    auto pw = std::make_unique<Widget>();
    
    // configure *pw;
    
    auto func = IsValAndArch(pw);
    
  • or with std::bind:

    auto pw = std::make_unique<Widget>();
    
    // configure *pw;
    
    auto func = std::bind( [](const std::unique_ptr<Widget>& pWidget)
        { return pWidget->isValidated() && pWidget->isArchived(); },
        std::move(pw) );
    

    Note that the parameter for the lambda is an lvalue reference (because the moved pw within the bind object is an lvalue) and has a const qualifier (to simulate the lambda's constness; so if the original lambda were declared mutable, the const qualifier would not be used).

Some details about init captures is also given in Anthony Calandra's cheatsheet of modern C++ language and library features, for instance that the initializing expression is evaluated when the lambda is created (not when it is invoked).

DaveFar
  • 7,078
  • 4
  • 50
  • 90
  • what is `pw - isValidated()`? What kind of `operator-` are you calling here? You're not using the initialised `pWidget` inside the lambda, so what's the point? – Walter Jan 07 '17 at 10:45
  • Thanks, Walter. I corrected the code (should be defined behavior now ;) – DaveFar Jan 07 '17 at 10:55
  • This is exactly the sort of answer I was hoping for. Thank you! – Omnifarious Jan 07 '17 at 11:00
  • I do have one question though, now that lambda captures can have non-trivial expressions in them, is there any rule on the order of evaluation of those expressions? I presume it's left undefined, like function arguments are, and that in fact individual expressions may even interleave with each other. – Omnifarious Jan 07 '17 at 11:10
  • 1
    @Omnifarious that's another, new questions. Post it. – Walter Jan 07 '17 at 11:18
  • @Omnifarious: I have added code examples for the two C++11 alternatives. – DaveFar Jan 07 '17 at 12:13
  • 1
    @Omnifarious: I do not know, but also presume that the order of evaluation of the expressions in an init capture is left undefined. However, individual expressions may no longer interleave with each other starting from C++14. – DaveFar Jan 07 '17 at 12:15
  • No more interleaving in C++14? Well, that's quite a change. Thank you. – Omnifarious Jan 07 '17 at 16:30
  • @DaveFar - Doesn't that start with C++17? http://stackoverflow.com/questions/38060436/what-are-the-new-features-in-c17/38060437#38060437 and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf – Omnifarious Jan 09 '17 at 03:23
  • @Omnifarious: Of course, C++17, you are right! There are also some interesting podcast episodes (cppcast and going native) about the interleaving restriction, and how it happened that order of evaluation is still undefined... – DaveFar Jan 09 '17 at 15:07
  • @DaveFar - Any links? – Omnifarious Jan 09 '17 at 17:46
  • @Omnifarious: I think it was some episode with Gabriel Dos Reis. – DaveFar Jan 12 '17 at 21:38