Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding C++0x lambda captures

Tags:

c++11

lambda

In one of the recent C++0x drafts (n3225.pdf) we can find 5.1.2/10:

The identifiers in a capture-list are looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression. An entity (i.e. a variable or this) is said to be explicitly captured if it appears in the lambda-expression’s capture-list.

That seems rather restrictive to me. For example, it seems to me that the following things are disallowed:

int global;

struct s {
    int x;
    void memfun() {
        [x,global]{};
    }
};

since x is not necessarily a variable with automatic storage and neither is global. Note that the intention of this capture clause is to let the lambda object store a copy of x and global which might be desirable in case they are changed at a later stage. I am already aware of an alternative:

int global;

struct s {
    int x;
    void memfun() {
        int copyx = x;
        int copyglobal = global;
        [copyx,copyglobal]{};
    }
};

But this boils down to extra copies and additional boiler plate just to capture x and global as copy.

Also, i cannot find anything conclusive in the latest drafts about what happens if we name a local reference in the capture clause:

int main() {
    int  i = 0;
    int &r = i;
    assert([r]{return &r;}() != &i);
}

Does the lambda object "copy a reference" or "copy an int"? If it captures the referred object by copy, this can save us the additional copies from the previous work-around.

GCC apparently supports all these examples and stores a copy of an int in the last case (which is desirable, IMHO). But I would like to know whether this is in fact the intended behaviour according to the C++0x drafts or just a compiler-extension respectivly an implementatin bug.

Edit:

templatetypedef pointed out 5.1.2/14 which explains what happens when a reference is named in a capture-clause. As far as I can tell, this allows us to use the following work-around for the first example:

int global;

struct s {
    int x;
    void memfun() {
        auto& cx = x;
        auto& cglob = global;
        [cx,cglob]{};
    }
};

Tia, sellibitze

like image 222
sellibitze Avatar asked Mar 29 '11 09:03

sellibitze


1 Answers

From what you've posted it seems like your first example is illegal since neither captured variable has automatic duration. However, you can easily fix this. To capture the data member, you can just capture this, and the global doesn't need to be captured as you can just reference it directly.

EDIT: As you pointed out, this will not create a local copy of the value you want to capture. To capture these variables while making a copy, you can capture this, then explicitly create a local copy of the data member inside of the lambda.

As for the second question about capturing references, §5.1.2/14 says that capturing a variable of reference type by copy will create a copy of the value referenced instead of creating a copy of the reference. Thus the lambda will have its own copy of the value that the reference was referencing when it was created.

like image 120
templatetypedef Avatar answered Oct 03 '22 13:10

templatetypedef