Lambda capture by value mutable не работает с const &?
Рассмотрим следующее:
void test( const int &value )
{
auto testConstRefMutableCopy = [value] () mutable {
value = 2; // compile error: Cannot assign to a variable captured by copy in a non-mutable lambda
};
int valueCopy = value;
auto testCopyMutableCopy = [valueCopy] () mutable {
valueCopy = 2; // compiles OK
};
}
Почему первая версия является ошибкой компиляции, когда я объявил лямбду изменяемой и захватил value по значению (которое, как я думал, сделало ее копию)?
Проверено с помощью clang (x86_64-apple-darwin14. 3. 0), откуда приходит сообщение об ошибке, и Visual C++ (vc120).
2 ответов:
[C++11: 5.1.2/14]:сущность захвачена копией если он неявно захвачен и capture-default является=или , если он явно захвачен с захватом, который не включает в себя&. Для каждой сущности, захваченной копией, в типе закрытия объявляется безымянный нестатический элемент данных. Порядок объявления этих членов не определен. тип такого элемента данных является типом соответствующей захваченной сущности, если сущность не является ссылкой на объект или ссылочный тип в противном случае. [..]Тип
Таким образом, даже если функция оператора вызова лямбды не являетсяvalueвнутри вашей лямбды -const int, потому что он был захвачен копией изconst int&.const(Вы отметили лямбдуmutable), фактический неявный членvalueимеет типconst intи не может быть изменен.Откровенно говоря, это кажется абсурдным; я ожидал бы, что это правило скажет, что ссылочный тип теряет
constНесс, поскольку это копия. Наличие или отсутствие ключевого словаmutableв самой лямбде (и, таким образом, наличие или отсутствие ключевого словаconstв сгенерированной функции оператора вызова) должно быть единственным контролем доступа здесь.В C++14 вы можете обойти это, захватив as
[value=value], который использует те же правила, что иauto, и таким образом отбрасываетconst. C++ - это здорово, не так ли?
mutableпозволяет лямбда-коду изменять копию неконстантного параметра, захваченного копией, но не разрешает его для параметровconst.Таким образом, этот код работает (и выводит
inside 2 outside 1):Но если мы опустимint a = 1; [a]() mutable { a = 2; // compiles OK cout << "inside " << a << "\n"; }(); cout << " outside " << a << "\n";mutableили сделаемconst int, компилятор выдаст ошибку.В нашем случае первая лямбда дает ошибку, потому что
valueявляетсяconst:void test( const int &value )Если мы сделаем
copyValueconst:const int valueCopy = value;Тогда та же ошибка произойдет и со второй лямбдой.
Comments