Лямбда: почему значения захвата по значению являются постоянными, а значения захвата по ссылке — нет?

Почему значения захвата по значению являются константными, а объекты захвата по ссылке — нет:

int a;

auto compile_error = [=]()
{
a = 1;
}
auto compiles_ok = [&]()
{
a = 1;
}

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

Так почему бы не захватить с помощью константной ссылки по умолчанию? Или, по крайней мере, поддержка &] а также [&]? Каковы причины этого дизайна?

В качестве обходного пути вы, вероятно, должны использовать std :: cref константные ссылки const, захваченные по значению?

7

Решение

Допустим, вы захватываете указатель по значению. Сам указатель является константным, но доступ к объекту, на который он указывает, — нет.

int i = 0;
int* p = &i;
auto l = [=]{ ++*p; };
l();
std::cout << i << std::endl;  // outputs 1

Эта лямбда эквивалентна:

struct lambda {
int* p;
lambda(int* p_) : p(p_) {}
void operator()() const { ++*p; }
};

const на operator()() делает использование p эквивалентно объявлению его как:

int* const p;

Аналогичная вещь происходит со ссылкой. Сама ссылка является «const» (в кавычках, потому что ссылки не могут быть повторно установлены), но доступ к объекту, на который она ссылается, отсутствует.

7

Другие решения

Захваченные ссылки являются также const, Или, скорее, ссылки всегда косвенным образом const — в языке нет синтаксиса, позволяющего изменить место, на которое указывает ссылка. a = 1; когда a является ссылкой, не изменяет ссылку, но меняет то, на что ссылается ссылка.

Когда вы говорите о «константной ссылке», я думаю, что вы сбиты с толку. Вы говорите о «ссылке на const int» (const int &). «Const» относится к тому, на что указывает ссылка, а не к самой ссылке. Это аналогично указателям: с указателем на const int (const int *) указатель сам по себе не const — Вы можете назначить переменной этого типа все, что вы хотите. Реальный «указатель констант» будет int *const, Здесь вы не можете назначить что-то такого типа; но вы можете изменить int это указывает на. Следовательно, «const» для указателя или ссылки отделен от «const» для того, на что он указывает. Вы также можете иметь «const указатель на const int»: const int *const,

2

Моя логика говорит следующее: лямбды — это просто кусок кода с необязательными необходимыми ссылками. В случае, когда вам нужно что-то скопировать (что обычно происходит для управления памятью, например, для копирования shared_ptr), вы все равно не хотите, чтобы лямбда имела свое собственное состояние. Это довольно необычная ситуация.

Я думаю, что только 2 следующие опции кажутся «правильными»

  • Вложите некоторый код, используя некоторые локальные переменные, чтобы вы могли «обойти его»
  • То же самое, что и выше, только добавить управление памятью, потому что, возможно, вы захотите выполнить этот фрагмент кода асинхронно или что-то еще, и область создания исчезнет.

Но когда лямбда является «изменчивой», то есть она захватывает значения, которые не являются постоянными, это означает, что она на самом деле поддерживает свою собственную государство. Смысл, каждый раз, когда вы звоните лямбда, это может привести к другому результату, который не основан на его фактическом закрытие, но опять же на его внутреннем государство, что является своего рода нелогичным в моей книге, учитывая, что лямбды происходят из функциональных языков.

Тем не менее, C ++, будучи C ++, дает вам возможность обойти это ограничение, сделав его немного уродливым, просто чтобы убедиться, что вы знаете, что делаете что-то странное.

Я надеюсь, что это связано с вами.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector