21

This is rejected by g++ (4.9.3 and 5.2.0), but is accepted by clang 3.5.0:

int main() { 
    const int ci = 0;
    auto lambda = [ &cap = ci ]() { };
}

g++ gives error: binding ‘const int’ to reference of type ‘int&’ discards qualifiers. It appears that g++ refuses to allow non-const references to be captured, except of course using plain old C++11 capture [&ci]. That seems a very strange constraint, perhaps a bug in g++?

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • 1
    https://stackoverflow.com/questions/3772867/lambda-capture-as-const-reference – Cory Kramer Aug 10 '15 at 12:19
  • @CoryKramer, that question is indeed very closely related. I commented on that question today also. I guess my question here is: "given a *const* object, how can I capture it by reference?". Whereas that question is "given a *non-const* object, how can I capture it by const reference?". I guess a good answer would cover both these questions. – Aaron McDaid Aug 10 '15 at 12:21

2 Answers2

12

Your code is valid. §5.1.2/11 goes

An init-capture behaves as if it declares and explicitly captures a variable of the form
auto init-capture ;
whose declarative region is the lambda-expression’s compound-statement […]

Now, clearly, declaring

auto &cap = ci;

and capturing cap is fine. That is,

int main() { 
    const int ci = 0;
    auto &cap = ci;
    auto lambda = [&cap]() { };
}

compiles with GCC. Apart from the declarative region and lifetime of cap, there is no difference between this snippet and yours, thus GCC is incorrect.
This bug has already been reported as #66735, with a similar example:

int x = 0;
auto l = [&rx = static_cast<const int&>(x)] {};
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • In that case, I'll report it soon. I'm extremely surprised that this bug wasn't noticed before - it seems to be such a simple example. Surely I'm not the first person to have tried to capture const objects by reference? – Aaron McDaid Aug 10 '15 at 12:35
  • @AaronMcDaid You aren't, but capturing a `const` object by reference using an init-capture is probably happening very rarely. – Columbo Aug 10 '15 at 12:42
  • [This report](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66735) is very similar. I might just comment on that bug with my slightly simpler example. – Aaron McDaid Aug 10 '15 at 12:42
  • @AaronMcDaid Thanks. I'll incorporate that into the answer. – Columbo Aug 10 '15 at 12:43
  • I found this while trying to capture a non-const object by const, by first const-casting it to const. But yes, capturing an object that is already const (i.e. my quesion here) would be very strange! – Aaron McDaid Aug 10 '15 at 12:44
  • @AaronMcDaid I've come across this in several contexts. E.g. trying to "drill into" a const reference parameter to a function in a lambda. E.g. `f(const Sprite& sprite)`. Lambda `[&rect = sprite.m_area]`, and, worse, any attempt to capture a member variable in a const member function. – TooTone Jul 26 '18 at 13:41
4

This looks similar to gcc bug: [C++14] lambda init-capture fails for const references which says:

This code fails to compile:

int main() {
    int x = 0;
    auto l = [&rx = static_cast<const int&>(x)]() {};
}

The error message is:

test.cpp:3:14: error: binding 'const int' to reference of type 'int&' discards qualifiers

auto l = [&rx = static_cast<const int&>(x)]() {

But according to [expr.prim.lambda]/11 rx should be captured as auto &rx = static_cast(x), that is as const int&.

the bug report references [expr.prim.lambda]/11 which says:

An init-capture behaves as if it declares and explicitly captures a variable of the form “auto init-capture ;” whose declarative region is the lambda-expression’s compound-statement, except that[...]

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740