Thomas1965 wrote: |
---|
I wonder if using a template parameter like the STL algorithms would give more flexibility than a lambda. |
It's impossible to write the type of a lambda. Do you mean
std::function<>
?
I guess you're talking about something like
1 2 3
|
template <typename Predicate>
World::Node* World::getHumanBy(Predicate&& predicate)
{ /*...*/ bool result = std::forward<Predicate>(predicate)(human); /* ... */ }
|
vs the OP's
World::Node* World::getHumanBy(std::function<bool(Human&)> const & predicate)
std::function
's purpose is to homogenize groups of callable objects, not to be used individually where the genericity isn't required; it's not free. For this reason it is usually preferrable to let duck typing do the work for you and use a template parameter.
For any given template parameter, the exact requirements on it depend on the function implementation which is ultimately selected, but if we assume that it involves just a direct call like above (i.e.,
std::forward<Predicate>(predicate)(human)
), then the template parameter
Predicate
must only satisfy
FunctionObject.
Remember, however, that
std::function
can bind to anything which satisfies
Callable with a particular signature. This is in fact more generic than the function template above.
Everything that satisfies
FunctionObject also satisfies
Callable, but the converse is not true. One example is member function pointers: they are
Callable, but their call syntax is idiosyncratic and giving one to the function template above would result in a substitution failure (because member-function pointers don't satisfy
FunctionObject). A contrived example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
# include <functional>
# include <iostream>
struct A {
void mfn() const { std::cout << "here! (" << this << ")\n"; };
void takes_callable(std::function<void(A*)> const& f) { f(this); }
template <typename F>
void takes_forwarding_ref(F&& f) { std::forward<F>(f)(this); }
};
int main() {
A a;
auto mfp = &A::mfn;
a.takes_callable(mfp);
// error from call req'd here: not a function
a.takes_forwarding_ref(mfp);
}
|
But it is possible to use a lambda, a bind expression, or something equivalent to do the right thing:
a.takes_forwarding_ref([=](auto* p) { return (p->*mfp)(); });
which implies that the difference in genericity is minor. Further, overload resolution means that if it was really necessary, you could specially handle member-function pointers, although at that point
std::function
may begin to look more attractive.