Ответ 1
Если вы хотите производительность, перейдите по значению, если вы его сохраняете.
Предположим, что у вас есть функция, называемая "запустить это в потоке пользовательского интерфейса".
std::future<void> run_in_ui_thread( std::function<void()> )
который запускает некоторый код в потоке "ui", затем сигнализирует future
по завершении. (Полезно в интерфейсах пользовательского интерфейса, где поток пользовательского интерфейса - это то место, где вы должны взаимодействовать с элементами пользовательского интерфейса)
Мы имеем две сигнатуры, которые мы рассматриваем:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Теперь мы можем использовать их следующим образом:
run_in_ui_thread( [=]{
// code goes here
} ).wait();
который создаст анонимное закрытие (лямбда), построит из него std::function
, передаст его функции run_in_ui_thread
, а затем дождитесь завершения работы в основном потоке.
В случае (A), std::function
напрямую строится из нашей лямбда, которая затем используется в run_in_ui_thread
. Лямбда move
d в std::function
, поэтому любое подвижное состояние эффективно переносится в нее.
Во втором случае создается временная std::function
, лямбда move
d, а затем временная std::function
используется ссылкой в run_in_ui_thread
.
До сих пор, так хорошо - они оба выполнялись одинаково. За исключением того, что run_in_ui_thread
собирается сделать копию своего аргумента функции для отправки в поток ui для выполнения! (он вернется, прежде чем он будет сделан с ним, поэтому он не может просто использовать ссылку на него). Для случая (A) просто move
std::function
в его долговременное хранение. В случае (B) мы вынуждены скопировать std::function
.
Этот магазин делает переход по значению более оптимальным. Если есть возможность сохранить копию std::function
, перейдите по значению. В противном случае, в любом случае, это примерно эквивалентно: единственная минус по значению - это то, что вы принимаете тот же громоздкий std::function
и используете один подпоточный метод за другим. При этом a move
будет таким же эффективным, как const&
.
Теперь есть некоторые другие различия между ними, которые в основном срабатывают, если у нас есть постоянное состояние в std::function
.
Предположим, что std::function
хранит некоторый объект с operator() const
, но также имеет некоторые элементы данных mutable
, которые он изменяет (как грубо!).
В случае std::function<> const&
измененные члены данных mutable
будут распространяться вне вызова функции. В случае std::function<>
они не будут.
Это относительно странный угловой случай.
Вы хотите лечить std::function
, как и любой другой, возможно, тяжелый, дешево-подвижный тип. Перемещение дешево, копирование может быть дорогостоящим.