Допустим, у меня есть функция, которая принимает std::function
:
void callFunction(std::function<void()> x)
{
x();
}
Должен ли я пройти x
по const-reference вместо этого ?:
void callFunction(const std::function<void()>& x)
{
x();
}
Меняется ли ответ на этот вопрос в зависимости от того, что функция делает с ним? Например, если это функция-член класса или конструктор, который сохраняет или инициализирует std::function
переменную-член.
sizeof(std::function)
это будет не более чем2 * sizeof(size_t)
, что является наименьшим размером, который вы когда-либо рассматривали бы для константной ссылки.std::function
оболочки так же важен, как сложность ее копирования. Если используются глубокие копии, это может быть намного дороже, чем можноsizeof
предположить.move
функцию в?operator()()
этоconst
так ссылка Const должна работать. Но я никогда не использовал std :: function.Ответы:
Если вы хотите производительность, передайте по значению, если вы храните его.
Предположим, у вас есть функция «запустить это в потоке пользовательского интерфейса».
который запускает некоторый код в потоке «ui», а затем сигнализирует о том,
future
когда это сделано. (Полезно в структурах пользовательского интерфейса, где поток пользовательского интерфейса находится там, где вы должны связываться с элементами пользовательского интерфейса)У нас есть две подписи, которые мы рассматриваем:
Теперь мы можем использовать их следующим образом:
который создаст анонимное закрытие (лямбда), создаст
std::function
из него, передаст егоrun_in_ui_thread
функции, затем дождется его завершения в главном потоке.В случае (A)
std::function
непосредственно создается из нашей лямбды, которая затем используется вrun_in_ui_thread
. Лямбда находитсяmove
вstd::function
, поэтому любое подвижное состояние эффективно переносится в нее.Во втором случае создается временный объект
std::function
, в него помещается лямбда-move
символ, тогда этот временный объектstd::function
используется ссылкой вrun_in_ui_thread
.Пока все хорошо - они работают одинаково. За исключением того,
run_in_ui_thread
что собирается сделать копию своего аргумента функции для отправки в поток пользовательского интерфейса для выполнения! (он вернется до того, как с ним будет покончено, поэтому он не может просто использовать ссылку на него). Для случая (А), мы просто в его длительного хранения. В случае (B) мы вынуждены копировать .move
std::function
std::function
Этот магазин делает переход по стоимости более оптимальным. Если есть вероятность, что вы храните копию
std::function
, передайте по значению. В противном случае, любой из этих способов примерно эквивалентен: единственным недостатком по значению является то, что вы берете одинstd::function
и тот же громоздкий и используете его один вспомогательный метод за другим. За исключением этого, amove
будет столь же эффективным, как иconst&
.Теперь есть некоторые другие различия между двумя, которые в основном включаются, если у нас есть постоянное состояние внутри
std::function
.Предположим, что
std::function
хранит некоторый объект сoperator() const
, но у него также есть некоторыеmutable
члены данных, которые он изменяет (как грубо!).В этом
std::function<> const&
случаеmutable
измененные члены-данные будут распространяться из вызова функции. Вstd::function<>
случае, они не будут.Это довольно странный угловой случай.
Вы хотите относиться так же,
std::function
как и к любому другому, возможно, тяжелому, дешевому подвижному типу. Перемещение это дешево, копирование может быть дорого.источник
const&
я вижу только стоимость операции копирования.std::function
создается из лямбды. В (A) временное исключается из аргумента дляrun_in_ui_thread
. В (B) ссылка на указанное временное сообщение передаетсяrun_in_ui_thread
. До тех пор, пока вашиstd::function
s создаются из лямбд как временных, этот пункт остается в силе. Предыдущий абзац касается случая, когдаstd::function
сохраняется. Если мы не храним, а просто создаем из лямбдыfunction const&
иfunction
имеем точно такие же накладные расходы.run_in_ui_thread()
. Есть ли просто подпись, чтобы сказать "Передать по ссылке, но я не буду хранить адрес"?std::future<void> run_in_ui_thread( std::function<void()>&& )
Если вы беспокоитесь о производительности и не определяете виртуальную функцию-член, то, скорее всего, вам вообще не следует ее использовать
std::function
.Создание типа функтора в качестве параметра шаблона допускает большую оптимизацию, чем
std::function
включение логики функтора. Эффект от этих оптимизаций, вероятно, значительно перевесит беспокойство копирования-против-косвенности относительно того, как пройтиstd::function
.Быстрее:
источник
std::forward<Functor>(x)();
для сохранения категории значений функтора, поскольку это «универсальная» ссылка. Хотя в 99% случаев это не поможет.callFunction(std::move(myFunctor));
std::move
если он вам больше не нужен, или перейти напрямую, если вы не хотите выходить из существующего объекта. Правила свертывания ссылок гарантируют, чтоcallFunction<T&>()
имеет параметр типаT&
, а неT&&
.Как обычно в C ++ 11, передача по значению / ссылке / const-ссылке зависит от того, что вы делаете со своим аргументом.
std::function
ничем не отличается.Передача по значению позволяет вам переместить аргумент в переменную (обычно переменную-член класса):
Когда вы знаете, что ваша функция переместит свой аргумент, это лучшее решение, так ваши пользователи смогут контролировать, как они вызывают вашу функцию:
Я полагаю, что вы уже знаете семантику (не) константных ссылок, поэтому я не буду вдаваться в подробности. Если вам нужно, чтобы я добавил больше объяснений по этому поводу, просто спросите, и я обновлю.
источник