Почему этот фрагмент кода работает?
#include <functional>
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> ptr(new int(666));
std::bind([](std::unique_ptr<int> &ptr){std::cout << *ptr << std::endl;}, std::move(ptr))();
}
Вы видите, что параметр лямбда должен быть ссылкой на lvalue, тогда как std::bind
передает ему rvalue (т.е. std::move(ptr)
).
Кроме того, почему этот фрагмент кода не компилируется?
std::function<void()> = std::bind([](std::unique_ptr<int> &ptr){std::cout << *ptr << std::endl;}, std::move(ptr));
Просто потому, что std::function
нужно скопировать все объекты, а std::move(ptr)
нельзя скопировать?
ОБНОВЛЕНО:
Вышеупомянутый код, который не удалось скомпилировать, виден по адресу https://localcoder.org/how-to-capture-a-unique-ptr-into-a-lambda-expression (т.е. см. «решение 3» в посте). Таким образом, указанное решение совершенно неверно. Я прав?
C++23
для меня невозможно. Что я могу использовать, так это C++17
.
Связано с @Джоном: Версия std::function только для перемещения, Как создать std::function из лямбда-выражения с захватом движения?
@John, для создания функции только для перемещения не так много кода. Предлагаю выложить в шапку и оставить // C++23: std::move_only_function
комментарий.
@RemyLebeau Код версии std::function
только для перемещения слишком сложен для меня, чтобы понять. :(
std::bind pass a rvalue ... to it
Да, rvalue было передано std::bind
. Он был должным образом спрятан для безопасного хранения до тех пор, пока не будет вызван связанный вызываемый объект.
В это время сохраненный объект передается лямбде по ссылке.
Другими словами, следующее является грубым упрощением, это то, что этот конкретный std::bind
примерно в конечном итоге делает что-то вроде этого:
struct bound_function {
template<typename Arg> bound_function(Arg &&arg)
: saved_parameter{std::forward<Arg>(arg)}
{
}
void operator()()
{
invoke_bound_function(saved_parameter);
}
private:
std::unique_ptr<int> saved_parameter;
void invoke_bound_function(std::unique_ptr<int> &ptr){std::cout << *ptr << std::endl;}
};
Действие привязки параметра к вызываемому объекту, а затем вызов вызываемого объекта с привязанным параметром — это два дискретных независимых события. В первом параметр перемещается в связанный объект как ссылка на rvalue.
Вызов связанной функции — это отдельный шаг, и связанный параметр передается связанной функции по ссылке.
Как правильно понять «Он был должным образом спрятан для безопасного хранения до тех пор, пока не будет вызван связанный вызываемый объект»?
На первый взгляд, есть функция-член, имя которой совпадает с именем класса, что меня смущает. :(
Его можно понимать буквально. Параметр, который был передан rvalue, не просто подвешивается, как зомби, в эфире, пока он не будет передан вызываемому объекту. Его нужно где-то сохранить. Затем, когда пришло время вызывать лямбду, она передается. А «функция, имя которой совпадает с классом», называется «конструктором». Одним из первых пунктов обсуждения в первой главе каждого учебника по C++, посвященной классам, является объяснение того, что такое конструктор и деструктор, как они работают, как их объявлять и как они используются.
std::bind()
не вызывает лямбду, он возвращает прокси, который вызовет лямбду, когда прокси будет вызван позже. Таким образом, только то, что ты передает ссылку rvalue в std::bind()
, не означает, что прокси передаст ссылку rvalue в лямбду.
А на самом деле, если подумать, прокси так и не может. Он должен переместить ваш объект unique_ptr
, на который ссылается rvalue, во что-то в прокси, чтобы сохранить его для последующего использования после выхода std::bind()
. И это что-то само по себе не является rvalue, и поэтому что-то может быть передано в лямбду по ссылке lvalue.
Спасибо, искренне. У меня есть связанный вопрос, пожалуйста, смотрите обновленный вопрос.
@Джон, я не могу ответить на твой другой вопрос. Это как-то связано с лямбда-выражениями без сохранения состояния (без захвата), которые до недавнего времени не копировались. Ваша версия gcc, вероятно, не поддерживает это.
«Просто потому, что std::function нужно копировать все объекты, тогда как std::move(ptr) не копируется?» Да, в таком случае вы можете использовать C++23
std::move_only_function
.