Подтвердить что ты не робот

Почему я не могу захватить эту ссылку ('& this') в лямбда?

Я понимаю, что правильный способ захвата this (для изменения свойств объекта) в лямбда выглядит следующим образом:

auto f = [this] () { /* ... */ };

Но я интересуюсь следующей особенностью, которую я видел:

class C {
    public:
        void foo() {
            // auto f = [] () { // this not captured
            auto f = [&] () { // why does this work?
            // auto f = [&this] () { // Expected ',' before 'this'
            // auto f = [this] () { // works as expected
                x = 5;
            };
            f();
        }

    private:
        int x;
};

Нечеткость, которую меня путают (и хотелось бы ответить), заключается в следующем:

auto f = [&] () { /* ... */ }; // capture everything by reference

И почему я не могу явно записать this по ссылке:

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.
4b9b3361

Ответ 1

Причина [&this] не работает, потому что это синтаксическая ошибка. Каждый параметр, разделенный запятыми, в lambda-introducer равен capture:

capture:
    identifier
    & identifier
    this

Вы можете видеть, что &this не допускается синтаксически. Причина, по которой это недопустимо, заключается в том, что вы никогда не захотите захватить this по ссылке, так как это небольшой указатель const. Вы бы только хотели передать его по значению - поэтому язык просто не поддерживает захват this по ссылке.

Чтобы захватить this явно, вы можете использовать [this] как lambda-introducer.

Первым capture может быть capture-default, который:

capture-default:
    &
    =

Это означает, что вы автоматически фиксируете все, что я использую, по ссылке (&) или по значению (=) соответственно - однако лечение this является особенным - в обоих случаях оно фиксируется по значению по указанным причинам ранее (даже с захватом по умолчанию &, который обычно означает захват по ссылке).

5.1.2.7/8:

Для поиска имени (3.4), определяя тип и значение this (9.3.2) и преобразовывая id- выражения, относящиеся к нестационарным членам класса, в выражения доступа к членам класса, используя (*this) (9.3.1), составной оператор [OF LAMBDA] рассматривается в контексте лямбда-выражения.

Итак, лямбда действует так, как если бы она была частью входящей функции-члена при использовании имен членов (например, в вашем примере использование имени x), поэтому он будет генерировать "неявные обычаи" this, как и функция-член делает.

Если лямбда-захват включает в себя захват по умолчанию, то есть &, идентификаторы в лямбда-захвате не должны быть которому предшествует &. Если лямбда-захват включает в себя захват-значение по умолчанию, равное =, лямбда-захват не должен содержать this, и каждому содержащемуся ему идентификатору предшествует &. Идентификатор или this не должен отображаться больше, чем один раз в лямбда-захвате.

Таким образом, вы можете использовать [this], [&], [=] или [&,this] как lambda-introducer для захвата указателя this по значению.

Однако [&this] и [=, this] плохо сформированы. В последнем случае gcc с благодарностью предупреждает для [=,this], что explicit by-copy capture of ‘this’ redundant with by-copy capture default, а не ошибки.

Ответ 2

Поскольку в списке Captures нет &this:

N4713 8.4.5.2 Захваты:

lambda-capture:
    capture-default
    capture-list
    capture-default, capture-list

capture-default:
    &
    =
capture-list:
    capture...opt
    capture-list, capture...opt
capture:
    simple-capture
    init-capture
simple-capture:
    identifier
    &identifier
    this
    * this
init-capture:
    identifier initializer
    &identifier initializer
  1. Для целей лямбда-захвата выражение потенциально ссылается на локальные объекты следующим образом:

    7.3 Это выражение потенциально ссылается на это.

Таким образом, стандартные гарантии this и *this действительны, а &this является недопустимым. Кроме того, захват this означает захват *this (который является значением lvalue, самим объектом) по ссылке, , а не захват this указателя по значению