У меня есть шаблонная функция, которая получает объекты-функции. Иногда объекты-функции являются структурами без состояний, но иногда они являются большими объектами с полным состоянием. Состояние функции-объекта не изменяется в этой функции, только проверяется. Я также очень заинтересован в написании кода, который компилятор может максимально оптимизировать. Что следует учитывать при выборе типа аргумента?
Функция имеет такой тип:
template<typename funcT>
auto make_particle(funcT fun) {
Particle<typename funcT::result_type> particle;
particle = fun();
return particle;
}
Тип аргумента, вероятно, должен быть funcT const & fun
чтобы крупные объекты не копировались, но почему большинство людей используют функциональные объекты с вызовом по значению? Я теряю что-то, используя константную ссылку? Или я должен использовать lvalue-reference? Обратите внимание, что c ++ 1y в порядке, и что приведенный выше пример кода является лишь примером.
Тип аргумента, вероятно, должен быть funcT const & весело, так что
крупные объекты не копируются,
Это не точка зрения алгоритмов в стандартных библиотеках. Там вызываемые объекты взяты по значению. Это зависит от автора вызываемого объекта, чтобы убедиться, что это разумно дешево копировать. Например, если ему нужен доступ к чему-то большому, вы можете попросить пользователя функтора предоставить ссылку на него и сохранить его в функторе — копирование ссылки обходится дешево.
Теперь может случиться так, что вы захотите сделать что-то не так, как в стандартной библиотеке, потому что вы ожидаете, что функции создания частиц будут необычайно трудными, чтобы сделать их дешевыми для копирования или перемещения. Но программисты на C ++ знакомы с копируемыми функторами, поэтому, если вы делаете то, что делает стандартная библиотека, обычно вы не делаете их жизнь хуже, чем они уже были. Копирование функторов не проблема, если вы не сделаете это один 🙂
Существует несколько вариантов использования, которые должны быть доступны:
Функтор не имеет состояния и поставляется как временный: make_particle(MyFun())
Функтор имеет состояние, которое необходимо восстановить позже: YourFun f; make_particle(f);
Вы не можете решить оба случая с одним единственным параметром ссылочного типа: в первом случае требуется ссылка const lvalue или ссылка rvalue, которая запрещает второе использование, а во втором случае требуется ссылка lvalue, которая запрещает первое использование.
Обычная идиома в таких ситуациях — принимать функтор по значению, и верни это в конце:
template <typename Iter, typename F>
F map(Iter first, Iter last, F f)
{
// ... f(*first) ...
return f;
}
Это может быть не совсем применимо в вашем случае, но это идея. Например, вы можете вернуть std::pair<ParticleType, F>
, В любом случае вам нужно, чтобы ваш тип функтора был копируемым, но это разумное требование.
Альтернатива, на которую охотно указывает @Xeo, и доступная для функции шаблоны только для получения аргумента функтора по универсальной ссылке, которая будет работать в обоих случаях:
template <typename Iter, typename F>
void map(Iter first, Iter last, F && f)
{
// ... use f(*first) ...
}
Обратите внимание, что в этом случае мы делаем не использование std::forward
, так как мы используем f
как подлинное упоминание, а не просто передать его где-то еще. В частности, нам не разрешено f
если мы все еще планируем использовать это.