What is the correct argument type for a function-object?

我的未来我决定 提交于 2019-12-05 04:40:29

The argument type should probably be funcT const & fun so that the large objects are not copied,

That is not the view taken by the algorithms in the standard libraries. There, callable objects are taken by value. It's up to the author of the callable object to ensure that it's reasonably cheap to copy. For example if it needs access to something large, then you can have the user of the functor provide a reference to one and store that in the functor -- copying a reference is cheap.

Now, it may be that you want to do things differently from the standard library because you expect particle-making functions to be unusually difficult to make cheap to copy or move. But C++ programmers are familiar with functors being copied, so if you do what the standard library does then usually you aren't making their lives any worse than they were already. Copying functors isn't an issue unless you make it one :-)

There are several use cases, which should all be available:

  • The functor has no state and is supplied as a temporary: make_particle(MyFun())

  • The functor has a state which needs to be recovered later: YourFun f; make_particle(f);

You cannot solve both cases with one single reference type parameter: The first case requires a const lvalue reference or an rvalue reference, which forbids the second use, and the second case requlres an lvalue reference, which forbids the first use.

A common idiom in such situations is to accept the functor by value, and return it at the end:

template <typename Iter, typename F>
F map(Iter first, Iter last, F f)
{
    // ... f(*first) ...
    return f;
}

That may not be entirely applicable in your case, though, but it's an idea. For example, you could return a std::pair<ParticleType, F>. In any case you'd require your functor type to be copyable, but that's a reasonable requirement.

An alternative, helpfully pointed out by @Xeo, and available for function templates only, is to take the functor argument by universal reference, which will work in both cases:

template <typename Iter, typename F>
void map(Iter first, Iter last, F && f)
{
    // ... use f(*first) ...
}

Note that in this case we do not use std::forward, since we are using f as a genuine reference, and not just to pass it through somewhere else. In particular, we are not allowed to move-from f if we still plan to use it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!