cppreference говорит , что std::function<R(Args...)>::operator()
имеет подпись
R operator()(Args... args) const;
и что он вызывает сохраненный вызываемый f
в основном f(std::forward<Args>(args)...)
. Характеристики производительности зависят как от аргумента шаблона, так и от типа аргумента лямбда, и я думаю, было бы полезно просто увидеть все , что может случиться. В вашем случае у вас есть 2 std::function
типов, 2 вызываемых объекта и 3 возможные категории значений для аргумента, что дает вам 12 возможностей.
std::function<void(VeryBigType)> f = [](VeryBigType i) { }
Если вы вызовете это с lvalue, например
VeryBigType v;
f(v);
, это скопирует v
в аргумент operator()
, а затем operator()
передаст rvalue лямбда , который переместит значение в i
. Общая стоимость: 1 копия + 1 ход
Если вы вызываете это с помощью prvalue, например
f(VeryBigType{});
, тогда это материализует prvalue в аргумент operator()
, затем передайте значение r лямбде, которое переместит его в i
. Общая стоимость: 1 ход
Если вы вызовете это с xvalue, например
VeryBigType v;
f(std::move(v));
Это переместит v
в аргумент operator()
, что передаст rvalue лямбде, которая снова переместит его в i
. Общая стоимость: 2 хода.
std::function<void(VeryBigType)> f = [](VeryBigType const &i) { }
Если вы вызовете это с lvalue, это будет скопировано один раз в аргумент operator()
, а затем лямбда будет дана ссылка на этот аргумент. Общая стоимость: 1 копия.
Если вы вызовете это с помощью prvalue, это материализует его в аргумент operator()
, который передаст ссылку на этот аргумент лямбде. Общая стоимость: ничего.
Если вы вызываете это с xvalue, это переместит его в аргумент operator()
, который передаст ссылку на этот аргумент лямбде. Общая стоимость: 1 ход.
std::function<void(VeryBigType const&)> f = [](VeryBigType i) { }
- Если вы вызываете это с lvalue или xvalue (т. Е. С glvalue ),
operator()
получит ссылку на него. Если вы вызовете это с prvalue, оно будет материализовано во временное, и operator()
получит ссылку на это. В любом случае внутренний вызов лямбды всегда будет копироваться. Общая стоимость: 1 копия.
std::function<void(VeryBigType const&)> f = [](VeryBigType const &i) { }
- Опять же, независимо от того, как вы это называете,
operator()
получит только ссылка на него, и лямбда просто получит ту же ссылку. Общая стоимость: ничего.
Итак, что мы узнали? Если и std::function
, и лямбда принимают ссылки, вы избегаете посторонних копий и перемещений. По возможности используйте это. Однако размещение лямбды по значению внутри ссылки by- const
-lvalue-reference std::function
- плохая идея (если только у вас нет to). По сути, ссылка lvalue «забывает» категорию значения аргумента, и аргумент лямбда всегда копируется. Помещение лямбда-выражения by- const
-lvalue-reference внутри побочного значения std::function
довольно хорошо с точки зрения производительности, но вам нужно это делать только в том случае, если вы вызываете другой код, который ожидает побочное значение std::function
, потому что в противном случае по ссылке std::function
достигается то же самое, но с меньшим количеством копий и перемещений. Помещение лямбды по значению внутри по значению std::function
немного хуже, чем размещение лямбды по значению const
-lvalue-reference внутри него из-за дополнительного перемещения во всех вызовах. Было бы лучше вместо этого взять аргумент лямбда по-rvalue-ссылке, что почти то же самое, что взять его по- const
-lvalue-reference, за исключением того, что вы все еще можете изменить аргумент, как если бы вы его взяли по значению в любом случае.
TL; DR: аргументы по значению и rvalue-ссылка в аргументе шаблона std::function
должны соответствовать аргументам по-rvalue-ссылке или по- const
-lvalue-ссылке в лямбда, которую вы помещаете в std::function
. Аргументы по lvalue-ссылке в типе должны соответствовать аргументам по lvalue-ссылке в лямбда. Все остальное требует дополнительных копий или перемещений и должно использоваться только при необходимости.