Понимание C ++ 0x лямбда-захватов - PullRequest
12 голосов
/ 29 марта 2011

В одном из последних проектов C ++ 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 в качестве копии.

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

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

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

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

Edit:

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

int global;

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

Тиа, sellibitze

1 Ответ

11 голосов
/ 29 марта 2011

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...