问题
I would like to make a function with a parameter that accepts either std::greater<int>
or std::less<int>
as the argument. I'm stuck on the syntax for the parameter, though.
This is the format I tried:
myFunction(int a, int b, bool *comp(int, int)) { … }
…
std::greater<int> bigger;
myFunction(2, 3, bigger);
That doesn't work, though, and I suspect the third parameter is just completely wrong. What should it actually be?
cannot convert
std::greater<int>
tobool* (*)(int, int)
回答1:
Functions taking a comparator are usually implemented via templates:
template <typename Comparator>
myFunction(int a, int b, Comparator comp) { … }
but you could also use std::function
to implement it:
myFunction(int a, int b, std::function<bool (int, int)> ) { … }
The first version exposes code in the header but will usually perform better. As for the second version, you can hide the implementation in the .cpp file, but you would lose some performance due to the impossibility to inline the comparator calls.
回答2:
So the trick here is that std::less
and std::greater
are actually stateless function objects that can be trivially constructed. But they don't support casting to a function pointer.
The efficient choices are either (A) take the comparator via template
argument and implement the code in a header:
template<typename C> void myFunc( int a, int b, C comp )
which means you have to implement it in a header file, or (B) type erase the function object via a std::function< bool(int, int) >
:
void myFunc( int a, int b, std::function< bool(int, int) > comp )
which has some costs (maybe significant? Profile!) (heap allocation is avoided via small object optimization for stateless std less/greater, but it tends to cost a virtual
function call regardless, which can also block inlining).
Or (C) write some code that lets you take a stateless functor and turn it into a function pointer:
template<typename T>
using Type = T;
template<typename StatelessFunctor>
struct function_ptr_of_stateless_t {
template<typename R, typename... Args>
operator Type<R(Args...)>*() const {
return [](Args... args)->R {
return StatelessFunctor()(std::forward<Args>(args)...);
};
}
};
template<typename StatelessFunctor>
function_ptr_of_stateless_t<StatelessFunctor> as_function_ptr() {
return {};
}
bool myFunction( int a, int b, bool(*comp)(int, int) ) { return comp(a,b); }
int main() {
std::cout << myFunction(3,7, as_function_ptr<std::less<int>>() ) << "\n";
}
where the template
function as_function_ptr
takes the type of your stateless functor and creates a throw away type that lets you cast it to any compatible function pointer type.
This has modestly less overhead than the std::function
solution, as a call over a function pointer tends to be faster than over a virtual
method, and in addition some compilers (like gcc) are quite decent at inlining function pointers, even from one compilation unit to another.
As a bonus, in C++14 you could use:
int main() {
std::cout << myFunction(3,7, as_function_ptr<std::less<>>() ) << "\n";
}
and it still works pretty optimally.
回答3:
Use a template:
template<class Callable>
myFunction(int a, int b, Callable f);
来源:https://stackoverflow.com/questions/22702435/parameter-to-use-stdgreater-or-stdless-as-argument