Pregunta

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> to bool* (*)(int, int)

¿Fue útil?

Solución

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.

Otros consejos

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.

Use a template:

template<class Callable>
myFunction(int a, int b, Callable f);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top