Значения лямбда-замыкания могут быть переданы как ссылочные параметры rvalue.

18

Я обнаружил, что lvalueлямбда-замыкания всегда можно передать в качестве rvalueпараметров функции.

Смотрите следующую простую демонстрацию.

#include <iostream>
#include <functional>

using namespace std;

void foo(std::function<void()>&& t)
{
}

int main()
{
    // Case 1: passing a `lvalue` closure
    auto fn1 = []{};
    foo(fn1);                          // works

    // Case 2: passing a `lvalue` function object
    std::function<void()> fn2 = []{};
    foo(fn2);                          // compile error

    return 0;
}

Случай 2 является стандартным поведением (я просто использовал std::functionдля демонстрационных целей, но любой другой тип будет вести себя так же).

Как и почему работает первый случай? Каково состояние fn1закрытия после возврата функции?

Sumudu
источник
5
Я думаю, это потому, что fn1неявно преобразуется std::functionв foo(fn1). Эта временная функция тогда является значением.
Айке
@RichardCritten Я действительно не был уверен, поэтому я не опубликовал ответ. Я думаю, что теперь нет необходимости в другом.
Айке
1
@ eike np Я часто чувствую то же самое, и много ответов.
Ричард Криттен
2
@Sumudu Человек, который задал вопрос, ввел вас в заблуждение, потому что они не знали, что они пытались спросить. Они хотели спросить: «Почему аргументы шаблона не могут std::functionбыть выведены из лямбды?». Ваша программа не пытается вывести аргументы шаблона std::function, поэтому нет проблем с неявным преобразованием.
eerorika
1
Название вопроса, на которое вы ссылаетесь, немного вводит в заблуждение. std::functionимеет неявный конструктор, который принимает лямбда-замыкания, поэтому существует неявное преобразование. Но в обстоятельствах связанного вопроса, создание шаблона std::functionне может быть выведено из лямбда-типа. (Например , std::function<void()>можно построить из [](){return 5;}даже если она имеет непустой тип возвращаемого значения .
Айке

Ответы:

8

Как и почему работает первый случай?

Вызов fooтребует, чтобы экземпляр std::function<void()>этого привязывался к rvalue-ссылке . std::function<void()>может быть построен из любого вызываемого объекта, который совместим с void()подписью.

Во-первых, временный std::function<void()>объект построен из []{}. Конструктор используется # 5 здесь , который копирует замыкание в std::functionслучае:

template< class F >
function( F f );

Инициализирует цель с std::move(f). Если fнулевой указатель на функцию или нулевой указатель на член, *thisпосле вызова будет пустым.

Затем временный functionэкземпляр привязывается к ссылке rvalue.


Каково состояние закрытия fn1 после возврата функции?

То же, что и раньше, потому что он был скопирован в std::functionэкземпляр. Первоначальное закрытие не затрагивается.

Витторио Ромео
источник
8

Лямбда это не а std::function. Ссылка не привязана напрямую .

Случай 1 работает, потому что лямбда-символы можно преобразовать в std::functions. Это означает, что временный std::functionматериал материализуется путем копирования fn1 . Указанный временный объект может быть привязан к rvalue-ссылке, поэтому аргумент соответствует параметру.

И копирование также является причиной того, fn1что на нее ничего не влияет foo.

Рассказчик - Unslander Monica
источник
5

Каково состояние закрытия fn1 после возврата функции?

fn1 без гражданства, так как он ничего не захватывает.

Как и почему работает первый случай?

Это работает, потому что аргумент имеет тип, отличный от типа, на который ссылается rvalue. Из-за наличия другого типа рассматриваются неявные преобразования. Поскольку лямбда может вызываться для аргументов этого std::function, она неявно преобразуется в нее через конструктор преобразования шаблонов из std::function. Результатом преобразования является prvalue, и, таким образом, его можно связать со ссылкой на rvalue.

eerorika
источник