What is the reason C++ function can’t be used as a type to be passed as a functor or predicate?
One of the main purposes of the functor is to behave as a function so that it could be called in a function manner. Well, it could do more (have states, etc.), but why we can’t use functions instead of functors it the sole purpose of the functor is to be called?
Well, the object should be constructed, as well for the functors, but this could be skipped if we see that the function was passed.
In the code below both function and functor behave similarly until I try to pass them to std::map.
#include <algorithm>
#include <map>
#include <ranges>
#include <span>
#include <vector>
bool std_span_less(const std::span<const unsigned int> lhs, const std::span<const unsigned int> rhs) {
return std::ranges::lexicographical_compare(lhs, rhs);
}
struct std_span_less_fn {
bool operator() (const std::span<const unsigned int> lhs, const std::span<const unsigned int> rhs) const {
return std::ranges::lexicographical_compare(lhs, rhs);
}
};
int main()
{
using fn_type = bool (*)(const std::span<const unsigned int> , const std::span<const unsigned int> );
std::vector<unsigned int> lexems = { 0, 1, 2 };
std_span_less_fn fn_obj;
auto span1 = std::span(&(lexems[0]), 1);
auto span2 = std::span(&(lexems[0]), 2);
auto res_fn = fn_obj(span1, span2); // Can be used as function
auto res = std_span_less(span1, span2); // Naturally used as function
std::map<std::span<const unsigned int>, int, std_span_less_fn> sentences_fn;
std::map<std::span<const unsigned int>, int, std_span_less> sentences; // Doesn't compile
sentences_fn[std::span(&(lexems[0]), 1)] = 0;
sentences_fn[std::span(&(lexems[2]), 1)] = 2;
sentences_fn[std::span(&(lexems[1]), 1)] = 1;
}
Is the only reason to restrict passing functions is this object construction or I am missing something else?
I read the documentation on the std::map but failed to find explicit statement what makes impossible to pass a function as Compare except my vague "Is function a type?" thoughts.
>Solution :
You can pass functions as a template paramter, but you need to pass it as a pointer to the function. The reason you need this is because a function type is exactly that, only a type, you can’t create an object from it like you can with functor. What you can do is have a pointer to a function and you can assign to that pointer what actual function it should point to. Doing that changes
std::map<std::span<const unsigned int>, int, std_span_less> sentences;
to
std::map<std::span<const unsigned int>, int, decltype(std_span_less)*> sentences{&std_span_less};
^ make a pointer type ^ ^^^^^^^^^^^^^^
|
initialize with a pointer to the function you want to use
You can alternatively use
std::integral_constant<decltype(&std_span_less), std_span_less>
as the compare type which wraps the function pointer value into the type so you do not need to initialize the map with the pointer. That changes the code to be:
std::map<std::span<const unsigned int>, int,
std::integral_constant<decltype(&std_span_less), std_span_less>> sentences;