Значення замикання лямбда можуть бути передані як референтні параметри 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для демонстраційних цілей, але будь-який інший тип поводився би так само).

Як і чому працює випадок 1? Який стан fn1закриття після повернення функції?


5
Я думаю, це тому fn1, що неявно перетворюється на std::functionв foo(fn1). Ця тимчасова функція тоді є релевантною.
eike

@RichardCritten Я дійсно не був впевнений, тому відповіді не опублікував. Я думаю, зараз немає потреби в іншому.
eike

1
@eike np Я часто відчуваю те саме, і ти багато відповідей.
Річард Кріттен

2
@Sumudu Людина, яка поставила запитання, ввела вас в оману, оскільки не знала, що намагаються задати. Що вони мали на меті запитати: «Чому аргументи шаблону не std::functionможна вивести з лямбда». Ваша програма не намагається вивести аргументи шаблону std::function, тому немає проблеми з неявним перетворенням.
eerorika

1
Назва питання, яке ви пов’язали, трохи вводить в оману. std::functionмає не явний конструктор, який приймає лямбда-закриття, тому відбувається неявна конверсія. Але в обставинах пов'язаного запитання про шаблонне уявлення std::functionне можна зробити з лямбда-типу. (Наприклад, std::function<void()>може бути побудований з [](){return 5;}того, що він має недійсний тип повернення.
eike

Відповіді:


8

Як і чому працює випадок 1?

Для виклику 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.


5

Який стан закриття fn1 після повернення функції?

fn1 є без громадянства, оскільки він нічого не фіксує.

Як і чому працює випадок 1?

Він працює, тому що аргумент відрізняється від типу, на який посилається rvalue. Через те, що вони мають інший тип, враховуються неявні перетворення. Оскільки лямбда викликається аргументами цього std::function, вона неявно перетворюється на неї через конструктор конвертування шаблонів std::function. Результат перетворення - первісне значення, і таким чином може бути пов'язаний з посиланням на rvalue.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.