2

I have been reading Lambda capture as const reference?
It is an interesting feature, and sometimes I also wish this feature to exist, especially when I have huge data, which I need to access inside a lambda function.

My follow up questions -

  • Should we capture by const reference in lambda? If yes, how should it be behave?
    (Edit - I am also interested in the behaviour of the lifetime of the captured variable.)
  • Is there any possible downside, by introducing it in the C++ grammar? (I can't think of any)

Let us assume we can.
We assume [const &] as syntax to capture.

int x = 10;
auto lambda = [const & x](){ std::cout << x << std::endl; }; 
lambda(); // prints 10, great, as expected

x = 11;
lambda(); // should it print 11 or 10 ?

My intuition is that is should behave just like [&] but the captured value should not be allowed to modify.

template<typename Func>
void higher_order_function(int & x, Func f)
{
    f(); // should print 11
    x = 12;
    f(); // should print 12
}

void foo()
{
    int x = 10;
    auto c = [const & x] () { std::cout << x << std::endl; };

    c(); // should print 10
    x = 11;
    c(); // should print 11
    higher_order_function(x, c);

    auto d = [const & x] () { x = 13; }; // Compiler ERROR: Tried to assign to const qualified type 'const int &'!
} 
badola
  • 820
  • 1
  • 13
  • 26
  • 2
    You are making a common mistake with the meaning of `const`. `const` doesn't describe a value -- only a particular symbol. It **does not** mean "the value won't change". It means "this particular symbol won't change the value" – Drew Dormann Jun 09 '18 at 22:57
  • When trying to compile (clang++ -std=c++14) I get this error: `expected variable name or 'this' in lambda capture list`. How can I compile this? (I removed the last statement). – Kostas Jun 10 '18 at 04:09

3 Answers3

4
lambda(); // should it print 11 or 10 ?

I don't understand why it should print 10. Consider lambda being just an instance of some anonymous class. Making it a normal class, it should look like:

class Lambda {
   public:
      Lambda(const int & i) : i_(i) { }
      void operator()() { std::cout << i_ << std::endl; }
   private:
      const int & i_;
 };

int x = 10;
Lambda lambda(x);
lambda(); // prints 10, great, as expected

x = 11;
lambda(); // should it print 11 or 10 ?

The meaning of const reference here is only that you cannot modify x through i_ member reference variable.


A simlpler scenario:

int x = 10;
const int & crx = x;
x++;
std::cout << crx << std::endl; // prints 11
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 1
    The question was, if there should be a language extension. – kiloalphaindia Jun 09 '18 at 22:39
  • 1
    @kiloalphaindia It was not exacly clear for me what OP is asking, since there are multiple questions. `// should it print 11 or 10 ?` this is a question as well. – Daniel Langr Jun 09 '18 at 22:42
  • @DanielLangr, thanks for the explanation. I am more interested in "Is there any possible downside, by introducing it in the C++ grammar?". Any comments or insight on this? – badola Jun 10 '18 at 04:22
4

I've been wondering about this myself.
Since the operator() is const by default, I would assume that allowing const references is acceptable as well.

With the current standards(C++17), the closest I got to this behaviour is:

auto c = [ &x = std::as_const(x) ] () { std::cout << x << std::endl; };

Workaround in C++11/C++14 would be(thanks to Daniel for suggestion):

auto const & crx = x;
auto c = [ &crx ] () { std::cout << crx << std::endl; };
JVApen
  • 11,008
  • 5
  • 31
  • 67
  • 5
    Just a note, this is valid from C++17. In C++11, you can alternatively do `const int & crx = x;` and capture `crx` then instead of `x`. – Daniel Langr Jun 09 '18 at 22:51
1

Maybe not exactly what you're looking for but... I suppose you can pass through a function that receive the same value by const reference.

Something as follows

template <typename T>
auto make_capture_const (T const & x)
 { return [&x](){ std::cout << x << std::endl; }; }

// ...

int x { 42 };

auto l = make_capture_const(x);

l();

If you try to modify x inside the lambda

std::cout << x++ << std::endl;

you should get an error.

As you can see, from, this solution you get that x can't be modified inside the lambda but the lambda is influenced from the value changes outside

   int x { 42 };

   auto l = make_capture_const(x);

   l();  // print 42

   x = 43;

   l();  // print 43

IMHO, an hypothetic [const &] capture syntax should works in the same way. But I understand that is highly questionable.

max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    In C++17, you can also use `auto lambda = [&x = std::as_const(x)]{ std::cout << x << std::endl; };`. – Daniel Langr Jun 09 '18 at 22:46
  • @DanielLangr - interesting; I did't know it. – max66 Jun 09 '18 at 22:51
  • @max66 , thanks for the explanation. I am more interested in "Is there any possible downside, by introducing it in the C++ grammar?". Any comments or insight on this? – badola Jun 10 '18 at 04:26
  • I guess the downside is that the lambda can't be passed out of the function scope if it captures by reference. If you don't need to modify the values, why restrict the portability? – Gem Taylor Jun 18 '18 at 14:01