Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda capture an array element failed

The following C++ code makes the compiler GCC(6.3.0) and Clang (3.8.0) mad.

for (auto v : vectors2d)
    for_each (begin(ret), end(ret), [v[3]] (int &n) { n+= v[3];});

While the following is fine

for (auto v : vectors2d) {
    auto val = v[3];
    for_each (begin(ret), end(ret), [val] (int &n) { n+= val;});
}

I know in C++ 14 I can do something like

for (auto v : vectors2d)
    for_each (begin(ret), end(ret), [val=v[3]] (int &n) { n+= val;});

The error in GCC is

expected identifier before '[' token

Clang says

expected ',' or ']' in lambda capture list

My question is: Why it is illegal for [v[3]] to appear in a capture list?

like image 426
CS Pei Avatar asked Jun 11 '17 14:06

CS Pei


2 Answers

My question is why it is illegal for [v[3]] in capture list?

As described in 5.1.2/1 [expr.prim.lambda] in N4141, the items in a capture list shall be either a simple-capture or an init-capture.

The former is any of

  • identifier
  • & identifier
  • this,

the latter either identifier initializer or & identifier initializer.

v[3] fits none of the above and is thus correctly rejected by the compiler.

like image 57
Baum mit Augen Avatar answered Sep 28 '22 19:09

Baum mit Augen


v[3] is not a variable - it's a complex expression which unfolds to *(v + 3) (if operator[] is not overloaded). So, capturing v[3] is very similar in its spirit to capturing of x * x + y * y - and that makes much less sense. E.g. the compiler would have to accept x * x + y * y inside the lambda, but sometimes reject y * y + x * x because overloaded operators do not have to be commutative.

Basically, you ask compiler: "if I use an expression equivalent to what I captured, it's ok, but if I mix variables the other way around, you should give me compiler error".

Suppose v[3] was legal. Then all of the following lambdas should be compiled correctly:

[v[3]]() { return v[3]; }
[v[3]]() { return v[2 * 2 - 1]; }
[v[3]](int x) { assert(x == 3); return v[x]; }

So if we want "invalid capture" to be a compiler error, the compiler should be able to somehow "prove" that we're not going to access any element in v other than v[3]. This is harder than the halting problem, so it's impossible.

We could, of course, make some less strict limitations: e.g. allow only v[3], but not v[2 * 2 - 1] or create some algorithm for detection of such cases that work "good enough", but sometimes provide false negatives. I don't think it's worth the effort - you can always "cache" the expression inside a variable and capture that by value.

like image 39
yeputons Avatar answered Sep 28 '22 21:09

yeputons