Cannot capture reference in lambda from universal reference?

384 views Asked by At

I am trying to figure out why the captured variable cannot be passed to the foo function by reference, the program shown below does not compile with the following error:

error: invalid initialization of reference of type 
'Object&' from expression of type 'const Object'

Is there some way to perfect forward the universal reference into the lambda so that it is captured by reference for later use? Types T might also be R-Values or a combination of R-Values and L-Values, so they cannot always be captured by reference.

struct Object
{
};

void foo(Object& a)
{
}

template<typename... T>
auto bar(T&&... values)
{
    return [values...](){
        foo(values...);
    };
}

int main ()
{
    Object obj;
    auto lambda = bar(obj);
    return 0;
}
1

There are 1 answers

3
bipll On

You're by capturing by values which stores values as const members inside the closure, and they cannot bind to non-const lvalue references. For capture by-ref you've forgot to put '&' before values:

return [&values...](){
    foo(values...);
};

A small example.

The only diff between the two files:

diff omg.cpp nyan.cpp 
6c6
<   return [values...]{ foo(values...); };
---
>   return [&values...]{ foo(values...); };

Compile the one with by-value capture:

$ g++ -std=c++14 omg.cpp && ./a.out 
omg.cpp: In instantiation of ‘bar(T&& ...)::<lambda()> [with T = {int&}]’:
omg.cpp:6:16:   required from ‘struct bar(T&& ...) [with T = {int&}]::<lambda()>’
omg.cpp:6:38:   required from ‘auto bar(T&& ...) [with T = {int&}]’
omg.cpp:11:16:   required from here
omg.cpp:6:25: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
  return [values...]{ foo(values...); };
                  ~~~^~~~~~~~~~~
omg.cpp:3:6: note:   initializing argument 1 of ‘void foo(int&)’
 void foo(int &a) { std::cout << a << std::endl; }
  ^~~

Compile the other:

$ g++ -std=c++14 nyan.cpp && ./a.out 
42
314