Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
165 views
in Technique[技术] by (71.8m points)

c++ - C++11 lambda returning lambda

this piece of code is not something unknown to JS developers

function get_counter()
{
    return (
        function() {
            var c = 0;
            return function() { return ++c; };
        })();
}

it basically creates a which creates different enumerators. So I was wondering if same thing can be done in C++11 with new lambda semantics? I ended up writing this piece of C++ which unfortunately does not compile!

int main()
{
    int c;
    auto a = [](){
        int c = 0;
        return [&](){
            cout << c++;
        };
    };
    return 0;
}

so I was wondering if there is a workaround to get it compiled and if there is how can compiler make this code run correctly? I mean it has to create separate enumerators but it should also collect garbage (unused c variables).

by the way I'm using VS2012 compiler and it generates this error:

Error   2   error C2440: 'return' : cannot convert from 'main::<lambda_10d109c73135f5c106ecbfa8ff6f4b6b>::()::<lambda_019decbc8d6cd29488ffec96883efe2a>' to 'void (__cdecl *)(void)'    c:usersalidocumentsvisual studio 2012projectsestestmain.cpp   25  1   Test
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your code has a bug in that it contains a dangling reference; the c reference will refer to the local variable in the outer lambda, which will be destroyed when the outer lambda returns.

You should write it using a mutable by-value lambda capture:

auto a = []() {
    int c = 0;
    return [=]() mutable {
        cout << c++;
    };
};

This relies on a post-standard extension to allow multiple statements in a return-type-deducing lambda; Is there a reason on not allowing lambdas to deduce the return type if it contains more than one statement? The easiest way to fix it is to supply a parameter so that the lambda contains only a single statement:

auto a = [](int c) {
    return [=]() mutable {
        cout << c++;
    };
};

Unfortunately default parameters aren't allowed in lambdas, so you'd have to call this as a(0). Alternatively at the cost of readability you could use a nested lambda call:

auto a = []() {
    return ([](int c) {
        return [=]() mutable {
            cout << c++;
        };
    })(0);
};

The way this works is that when a executes the inner lambda copies all the referenced variables into an instance of its closure type, which here would be something like:

struct inner_lambda {
    int c;
    void operator()() { cout << c++; }
};

The instance of the closure type is then returned by the outer lambda, and can be invoked and will modify its copy of c when called.

Overall, your (fixed) code is translated to:

struct outer_lambda {
    // no closure
    struct inner_lambda {
        int c;    // by-value capture
        // non-const because "mutable"
        void operator()() { cout << c++; }
    }
    // const because non-"mutable"
    inner_lambda operator()(int c) const {
        return inner_lambda{c};
    }
};

If you left c as a by-reference capture, this would be:

struct outer_lambda {
    // no closure
    struct inner_lambda {
        int &c;    // by-reference capture
        void operator()() const { cout << c++; } // const, but can modify c
    }
    inner_lambda operator()(int c) const {
        return inner_lambda{c};
    }
};

Here inner_lambda::c is a dangling reference to the local parameter variable c.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...