Почему нельзя модифицировать захваченные по значению переменные в лямбде?

Предположим у нас есть класс:

class Probe
{
public:
   Probe(){}
   Probe(const Probe &aData){}
   Probe(Probe &&aData){}
   void setA(int aA) {a = aA;}
private:
   int a = 0;
};


int main()
{
    Probe vProbe;
    auto foo = [vProbe]() {vProbe.setA(5); };
    foo();
    return 0; 
}

Вот этот код компилироваться не будет так как пишется что vProbe это константа, но если передавать по ссылке то компилируется без проблем. Так почему такое возможно? Если передается копия объекта и по идее нет никакого основания делать эту копию константой.


Ответы (1 шт):

Автор решения: KoVadim

Потому что лямбда - это просто синтаксический сахар для класса с оператором круглые скобки. И если ничего не делать, то он помечен как const функция. А то, что передано в квадратных скобках будет "переменными этого класса". А если функция класса помечена const, то модифицировать переменные класса нельзя. Если добавить mutable, то const не добавляется.

Почему было сделано именно так? Видимо для оптимизаций.

UPD Найдено оригинальное объяснение от Саттера - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3424.pdf

This feature appears to have been added out of a concern that the user might not realize he got a copy, and in particular that since lambdas are copyable he might be changing a different lambda’s copy

То есть, "пользователь может не понимать, что это копия и модифицируя ее, он будет не понимать, что происходит".

END OF UPD

То есть, Ваша лямбда это

class __lambda_20_16
{
  public: 
  inline /*constexpr */ void operator()() const // ключевое!!!
  {
    vProbe.setA(5);
  }

  private: 
  Probe vProbe;
  public: 
  // inline __lambda_20_16 & operator=(__lambda_20_16 &&) = delete;
  __lambda_20_16(const Probe & _vProbe)
  : vProbe{_vProbe}
  {}

};

Есть чудесный сервис - https://cppinsights.io/ там можно посмотреть, как именно оно раскрывается. Правда с не компилируемым кодом оно не работает:)

→ Ссылка