0

Is there a reason why I cannot pass a comparison functor to a map as a constructor argument:

map<int, string, greater<int>> foo;
//map<int, string> foo(greater<int>()); Doesn't work

Or why I cannot pass a lambda without providing my own comparison type:

map<int, string, function<bool(const int&, const int&)>> bar([](const int& lhs, const int& rhs){ return lhs > rhs; });
//map<int, string> bar([](const int& lhs, const int& rhs){ return lhs > rhs; }); Doesn't work

I'd like to just be able to declare map<int, string> and construct it with a comparator. Why can't I?

[Live Example]

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Because that would involve some type-erasing, which is not optimal? – Piotr Skotnicki Mar 02 '16 at 20:28
  • Possible duplicate: [Why not infer template parameter from constructor?](http://stackoverflow.com/questions/984394/why-not-infer-template-parameter-from-constructor) – NathanOliver Mar 02 '16 at 20:33
  • @PiotrSkotnicki You're saying that for the functor version? But doesn't `map` provide a comparison constructor? http://www.cplusplus.com/reference/map/map/map/ Isn't that exactly what this is for? – Jonathan Mee Mar 02 '16 at 20:36
  • @JonathanMee it's used to initialize the actual comparator stored in a map. how would you store that comparator without knowing its type outside of the constructor – Piotr Skotnicki Mar 02 '16 at 20:38
  • @NathanOliver You're saying that we have to have the type in the `map` template because we could just use a pointer? – Jonathan Mee Mar 02 '16 at 20:41
  • @JonathanMee Yes. How would you know what kind of map it is? Remember a template class is not a class it is a recipe. We need to know at compile time which kind of map to make and having a pointer an initializing it a run time would circumvent that. – NathanOliver Mar 02 '16 at 20:43
  • @PiotrSkotnicki I see what you're saying. I'm thinking of functors as though they are `bool operator(const int&, const int&)` functions, but in fact `less` and `greater` are completely different structs. That sounds like a reasonable answer if you want to post it. – Jonathan Mee Mar 02 '16 at 20:44
  • @PiotrSkotnicki I have gone ahead and posted an answer of what I extrapolated from your comments. If you feel like posting an answer let me know, and I'll remove mine to defer to yours. – Jonathan Mee Mar 07 '16 at 12:48

1 Answers1

1

This question stems from a misconception. To clear that up:

Functors are objects not functions

Trying to assign a function pointer or a lambda to an object doesn't make any sense. So this cannot be done: map<int, string> bar([](const int& lhs, const int& rhs){ return lhs > rhs; }); The way to define a map which takes a function pointer or lambda is to use the template arguments from the question: map<int, string, function<bool(const int&, const int&)>>

Halfway between the two ill-conceived options in the question is another misconception: map<int, string, [](const int& lhs, const int& rhs){ return lhs > rhs; }> Doesn't work because the comparator template argument is the type of a member of map, not the intialization value. So a map using a function pointer or lambda comparator must always have that value passed to the map constructor: map<int, string, function<bool(const int&, const int&)>> bar([](const int& lhs, const int& rhs){ return lhs > rhs; }) Otherwise function<bool(const int&, const int&)>() would be used for comparisons in the map.

This is probably already clear by now, but since functors are objects you cannot pass an unrelated object is the construction value of a completely different type of object. Calling map<int, string> foo(greater<int>()) is like calling less<int> foo = greater<int>. The only acceptable compatator constructor argument to a map whose comparator template argument is a functor is something that can be converted into an object of the functor type in the template argument: map<int, string, greater<int>> foo(greater<int>{}) This is obviously unnecessary, because if no argument was provided and the greater<int> was default constructed the same member initialization of the map would result, so map<int, string, greater<int>> is sufficent.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288