Лямбда-захват захвата rvalue ссылка по ссылке - PullRequest
3 голосов
/ 07 апреля 2019

Код ниже стандартно-правильный? ( godbolt )

т.е. by-ref захватывает ссылку пересылки, которая представляет временный объект, и возвращает результирующее лямбда-значение из функции в том же выражении .

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

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

#include <stdlib.h>
#include <string.h>
#include <cassert>

template<typename F>
auto invoke(F&& f)
{
    return f();
}

template<typename F>
auto wrap(F&& f)
{
    return [&f]() {return f();}; // <- this by-ref capture here
}

int main()
{
    int t = invoke(wrap(
        []() {return 17;}
    ));

    assert(t == 17);
    return t;
}

Ответы [ 2 ]

2 голосов
/ 07 апреля 2019

В вашем коде был UB для относительно короткого окна.В первоначальных правилах лямбда-захвата по ссылке указывалось, что ссылка действительна только до тех пор, пока захваченная переменная не выйдет из области видимости.

Это может привести к некоторому виду захвата по ссылке,иначе невозможно в стандарте C ++.(Самое близкое, что вы могли бы получить, это ссылка на структуру с одним членом, содержащую ссылку)

Теоретически, вы можете использовать этот факт, чтобы сделать лямбда-захват захвата ссылок основанным на стековом фрейме;захватить текущий кадр стека, и все (почти?) аргументы ссылки-ссылки будут иметь фиксированные смещения к этому кадру стека.

Поскольку большинство (все?) ABI реализуют ссылочные аргументы как указатели под капотом, это будетприводить к ссылочным аргументам к функциям-аргументам, которые являются ссылками, висящими после того, как вернулась лямбда.

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

Когда он был обнаружен, он был разрешен как исправление дефекта.в стандарте, что означает, что он задним числом переопределил то, что означало .

Так что в то время как при историческом компиляторе это было технически UB, никакого текущегосовместимый компилятор может рассматривать его как UB, и все исторические компиляторы C ++ 11 обрабатывают его так же, как и современные компиляторы.Так что вы в безопасности.

2 голосов
/ 07 апреля 2019

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

В вашем случае захваченный f является ссылкой на лямбду (вместо параметра f).

...