2014-01-20 4 views
8

У меня есть шаблонная функция, которая получает функции-объекты. Иногда функции-объекты являются ассемблерными структурами, но иногда они являются большими объектами statefull. Состояние функции-объекта не изменяется в этой функции, а только проверяется. Я также очень хочу писать код, который компилятор может оптимизировать как можно больше. Что следует учитывать при выборе типа аргумента?Каков правильный тип аргумента для объекта-объекта?

Функция этого типа:

template<typename funcT> 
auto make_particle(funcT fun) { 
    Particle<typename funcT::result_type> particle; 
    particle = fun(); 
    return particle; 
} 

Аргумент типа, вероятно, следует funcT const & fun так, что крупные объекты не копируется, но почему большинство людей используют вызов по значению объектов функции? Я что-то теряю, используя ссылку const? Или я должен использовать lvalue-reference? Обратите внимание, что C++ 1y в порядке и что пример кода, приведенный выше, является просто примером.

ответ

5

Аргумент типа, вероятно, следует FUNCT Const & весело, так что большие объекты не копируются,

Это не вид, выполненный алгоритмами в стандартных библиотеках. Там вызываемые объекты берутся по значению. Это зависит от автора вызываемого объекта, чтобы гарантировать, что он разумно дешево копировать.Например, если ему нужен доступ к чему-то большому, то вы можете заставить пользователя функтора предоставить ссылку на него и сохранить его в функторе - копирование ссылки дешево.

Теперь возможно, что вы хотите делать что-то иначе, чем стандартная библиотека, потому что вы ожидаете необычайно сложной работы по созданию частиц, чтобы сделать ее дешевой для копирования или перемещения. Но программисты на С ++ знакомы с копированием функторов, поэтому, если вы делаете то, что делает стандартная библиотека, обычно вы не делаете свою жизнь хуже, чем они были. Копирование функторов не является проблемой, если вы не сделаете ее одной :-)

7

Есть несколько вариантов использования, которые все должны быть доступны:

  • функтор не имеет никакого состояния и поставляется в качестве временного: make_particle(MyFun())

  • функтор имеет состояние, которое должно быть восстановлены позже: YourFun f; make_particle(f);

Вы не можете решить оба случая с одним параметром одного опорного типа: первый случай г эквивалент ссылки на константу или ссылку на 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, если мы все еще планируем использовать его.

+1

Вы можете легко решить оба случая с одним эталонным параметром - T &&, aka universal reference. – Xeo

+0

@Xeo: Первоначально я думал о предоставлении единственной * функции *, а не шаблона, поэтому я искал одно решение. Вы правы, конечно, что, поскольку у нас уже есть шаблоны, мы могли бы также использовать их. –

Смежные вопросы