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

Понимание С++ 0x лямбда-захватов

В одном из недавних черновиков С++ 0x (n3225.pdf) мы можем найти 5.1.2/10:

Идентификаторы в списке захвата просматриваются с использованием обычных правил для поиска неквалифицированных имен (3.4.1); каждый такой поиск должен найти переменную с автоматическим временем хранения, объявленным в области охвата локального выражения лямбда. Сущность (т.е. Переменная или эта) называется явно захваченной, если она появляется в списке захвата лямбда-выражений.

Это кажется мне довольно ограничивающим. Например, мне кажется, что следующие вещи запрещены:

int global;

struct s {
    int x;
    void memfun() {
        [x,global]{};
    }
};

так как x не обязательно является переменной с автоматическим хранилищем, и не является global. Обратите внимание, что намерение этого предложения захвата состоит в том, чтобы позволить лямбда-объекту хранить копию x и global, которая может быть желательной, если они будут изменены на более позднем этапе. Я уже знаю альтернативу:

int global;

struct s {
    int x;
    void memfun() {
        int copyx = x;
        int copyglobal = global;
        [copyx,copyglobal]{};
    }
};

Но это сводится к дополнительным копиям и дополнительной плите котла только для захвата x и global в качестве копии.

Кроме того, я не могу найти ничего убедительного в последних черновиках о том, что произойдет, если мы назовем локальную ссылку в предложении capture:

int main() {
    int  i = 0;
    int &r = i;
    assert([r]{return &r;}() != &i);
}

Является ли лямбда-объект "копировать ссылку" или "копировать int"? Если он захватывает выделенный объект путем копирования, это может сэкономить нам дополнительные копии из предыдущего обхода.

GCC, очевидно, поддерживает все эти примеры и сохраняет копию int в последнем случае (что желательно, IMHO). Но я хотел бы знать, является ли это на самом деле предполагаемым поведением в соответствии с черновиками С++ 0x или просто с расширением-компилятором относительно ошибки реализации.

Edit:

templatetypedef указал 5.1.2/14, в котором объясняется, что происходит, когда ссылка упоминается в предложении capture. Насколько я могу судить, это позволяет использовать следующий пример для первого примера:

int global;

struct s {
    int x;
    void memfun() {
        auto& cx = x;
        auto& cglob = global;
        [cx,cglob]{};
    }
};

Тиа, sellibitze

4b9b3361

Ответ 1

Из того, что вы опубликовали, кажется, что ваш первый пример является незаконным, поскольку ни захваченная переменная не имеет автоматической продолжительности. Однако вы можете легко исправить это. Чтобы захватить элемент данных, вы можете просто зафиксировать это, и глобальное не нужно захватывать, поскольку вы можете просто ссылаться на него напрямую.

EDIT. Как вы указали, это не создаст локальную копию значения, которое вы хотите захватить. Чтобы захватить эти переменные при создании копии, вы можете ее захватить, а затем явно создать локальную копию элемента данных внутри лямбда.

Что касается второго вопроса о захвате ссылок, то в разделе 5.1.2/14 говорится, что захват переменной ссылочного типа путем копирования создаст копию ссылочного значения вместо создания копии ссылки. Таким образом, лямбда будет иметь свою собственную копию значения, на которое ссылалась ссылка, когда она была создана.