Безопасно ли передавать обратный вызов std:: function<bool (std:: string)> && (т.е. как перемещение по значению) и каков эффект?
Учитывая следующий рабочий код (main.cpp):
#include <functional>
#include <iostream>
struct worker
{
std::function<bool(std::string)> m_callback;
void do_work(std::function<bool(std::string)> callback) // <--- this line
{
m_callback = std::bind(callback, std::placeholders::_1);
callback("hello world!\n");
}
};
// pretty boring class - a cut down of my actual class
struct helper
{
worker the_worker;
bool work_callback(std::string str)
{
std::cout << str << std::endl;
return false;
}
};
int main()
{
helper the_helper;
the_helper.the_worker.do_work( [&](std::string data){ return the_helper.work_callback(data); });
}
Составлено с: -std=c++11 -O2 -Wall -Wextra -pedantic-errors -O2 main.cpp
Я прокомментировал обсуждаемую строку (<-- this line
- вокруг строки 7), где я думаю, что было бы более эффективно использовать: void do_work(std::function<bool(std::string)>&& callback)
то есть используя &&
двигаться семантически.
Я никогда этим не пользовался, в основном потому, что до сих пор не совсем понимаю.
Мое понимание таково:
void do_work(std::function<bool(std::string)> callback)
- возьму копию лямбды, которую я передаю (я думаю, это значение).
void do_work(std::function<bool(std::string)> callback)
- переместит лямбду, которую я передаю, потому что это значение.
Моя грубая идея о rvalue - любая временная переменная.
Вопросы:
То, что я не на 100% ясен о том, правильно ли я написал? и поэтому безопасно использовать
&&
, Оба, кажется, работают.Является ли это
&&
Метод также работает, если вместо передачи лямбда-кода, как это:
the_helper.the_worker.do_work( [&](std::string data){ return the_helper.work_callback(data); });
переходим в std::bind(...):
the_worker.do_work(std::bind(&helper::work_callback, the_helper, std::placeholders::_1));
2 ответа
Если параметр определен как rvalue-reference, вы должны передать временное значение или привести lvalue к rvalue, как с std::move()
,
И семантика rvalue-ссылок такова, что вызывающая сторона должна ожидать, что переданный аргумент будет разграблен, что сделает его действительным, но произвольным, что означает в основном бесполезный.
Но функция, получающая rvalue-ссылку, несмотря на то, что имеет лицензию на грабеж, не имеет никаких обязательств грабить. И если это явно не делается, например, путем передачи этой лицензии, то это не происходит, и ничего особенного не происходит.
Ваш код такой случай.
Хотя я бы запретил std::bind
Судя по моему словарному запасу, использовать его или нет не имеет никакого существенного значения.
В этом случае, независимо от того, передаете ли вы по значению или по rval ref временную std:: function, нужно будет создать это потому, что лямбда на самом деле не является std::function. В любом случае, вы должны переместить функцию std:: перед назначением, чтобы избежать создания ненужной копии.
В этом случае я бы рекомендовал передавать по значению, поскольку это немного более гибко, и если вы передаете лямбда-выражения, то это не причинит никакого вреда, так как функция std:: обычно создается на месте (поэтому временная не будет перемещен в функцию; этот шаг может быть и обычно будет отменен).