Parameter to use std::greater or std::less as argument

孤街醉人 提交于 2020-01-13 07:55:09

问题


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)


回答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

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