The existing answers will make code that works, but as a rule, with templated functions, you don't want them to be templated on function pointers, because that inhibits optimizations. In this case, inhibiting optimizations is a (relatively) big deal, because your desired default case makes your "compare" function (really a predicate function, since it's testing a condition on a single argument, not comparing multiple arguments) a no-op; if properly optimized, the default use of your function should not call your "always true" predicate at all, because it would inline the constant result, and simplify your test condition.
To properly generate optimized templates, you want to pass functors or C++11
lambdas (which are largely syntactic sugar for functors) as your predicate. This also means making the default predicate a lambda or functor, so the default case is fully performant. Thanks to C++11 lambdas, this is pretty easy:
// Define a trivial predicate that return true for any int input
// lambdas can only be constexpr on C++17 and higher; for C++11/C++14, you can
// just declare it const, and optionally static in either case
constexpr auto alwaystrue = [](int) { return true; };
// The type of a lambda is accessible via decltype, make that the default type
// of Predicate, so you can make the default value of predicate alwaystrue itself
template<typename Predicate=decltype(alwaystrue)>
map<_NODE, int> Find(const _NODE& src, const int max, Predicate pred=alwaystrue) const {
This is a significantly better option than accepting a function pointer defaulting to nullptr
or an existing alwaystrue
function; accepting a function pointer defaulting to nullptr
means you need to test it for nullptr
over and over (or split the code path into "given predicate" vs. "not given predicate"; if you're lucky, the compiler might do that for you, but don't count on it), and accepting as a function pointer at all means the function call can't be omitted, and must be performed through a pointer, not a stable jump to a known function location, meaning all the function call setup/call/return/teardown overhead is paid every time, even if the work to be done is a constant return true;
.
By writing it this way, even minimal optimizations (-O1
) ensure that the default predicate of alwaystrue
is optimized away entirely (it behaves as if that specialization of Find
doesn't even include && cmp(j)
/&& pred(j)
). If the caller explicitly passes a function/function pointer, they'll usually (barring extreme link time optimizations) get the slower behavior associated with calls through function pointer but it will still work; if they know what they're doing, they'll pass a lambda or functor and get the same optimizations alwaystrue
gets (inlining the test to avoid function call overhead completely, though obviously it can't optimize the actual test itself unless its as a simple as alwaystrue
's test).
As an alternative approach that avoids spilling information about alwaystrue
to anyone who includes your header, you can define two versions of your function, one of which accepts the predicate, and the other of which does not. Done this way, you can hide alwaystrue
in your source file (or inline the raw lambda without naming it), where it won't be seen or shared with other compilation units. Done this way, you can define a non-defaulting templated function and declare (without defining) a non-templated function that doesn't take a predicate argument in your header:
template<typename Predicate>
map<_NODE, int> Find(const _NODE& src, const int max, Predicate pred) const {
map<_NODE, int> Find(const _NODE& src, const int max) const;
then in your source file (hidden from others including your header), you'd the trivial body of the non-templated function implemented in terms of the templated function:
map<_NODE, int> Find(const _NODE& src, const int max) const {
return Find(src, max, [](int) { return true; });
}
This approach is what the C++ standard library does with functions like std::sort
where the comparator is optional; template< class RandomIt > void sort( RandomIt first, RandomIt last );
is typically implemented as return sort(first, last, functor_to_compare_iterator_targets());
.