0

I have a comparison/ordering function that relates to a class. I can use it if I define it as a separate closure object. I would like to make it into a static method of the class it operates on so it is tidier. I guessed how to do this but I get an error that I can't interpret.

Generally I would like to know how to treat static methods as callable objects.

Minimal related example code (not working):

#include <set>

class MyClass {

    // More code here

    static auto compare(MyClass a, MyClass b)   {
        return a < b;
    }
};

int main() {
    std::set<MyClass, decltype(MyClass::compare)> s(MyClass::compare);
    return 0;
}

Update, I'm not sure if my example confused the issue, so I updated it.

user83455
  • 17
  • 3
  • `&MyClass::compare` – JohnFilleau Dec 04 '21 at 13:07
  • Note: C++17 allows you to write `auto s = std::set(&MyClass::compare);` thanks to CTAD. However I'd consider the following tidier than your option: `struct MyCompare { bool operator()(int a, int b) const { return a < b; } };` `auto s = std::set(MyCompare());` or pre C++17 `std::set s = MyCompare();` – fabian Dec 04 '21 at 13:19

2 Answers2

1

Couple of issues:

  • compare is private, make it public.
  • One must use & to get the address of functions.
#include <set>

class MyClass {
public:
    static auto compare(int a, int b)   {
        return a < b;
    }
};

int main() {
    std::set<int, decltype(&MyClass::compare)> s(&MyClass::compare);
    return 0;
}
Quimby
  • 17,735
  • 4
  • 35
  • 55
  • 2
    The & seems not to be required in the second MyClass::compare (but it still works with it) – user83455 Dec 04 '21 at 13:44
  • @user83455 Yes, it is not necessary but I consider it good practice. Free or static member functions decay to pointers but member methods won't and `&` would be necessary for them, not that a method would be useful here. – Quimby Dec 04 '21 at 14:22
1

Make the function public, and add & in decltype:

std::set<int, decltype(&MyClass::compare)>

I wouldn't consider this to be "tidier" though.

A functor occupies 0 bytes when used as a std::set comparator. But a function pointer (as in your example) occupies 4 or 8 bytes. It also forces you to pass the function to the set's constructor.

Using a function pointer this way only makes sense if you want to switch between different comparators at runtime.

If you do want a pointer, the class itself is unnecessary. You might as well use a free function.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • And, if you really want to encapsulate the comparator function, don't use a class, use a namespace. – Fureeish Dec 04 '21 at 13:14
  • Thank you for the extra advice. I take it your comment about bytes regards inlining? A closure object can be inlined (or maybe an equivalent optimisation being copy elided?) but a pointer to a method will not? Regarding "tidier", I appreciate there is no runtime difference by writing the subroutine as a method. It is based on my background from other languages, putting it lexically inside the class intends to make it clear it relates exclusively to the class / more difficult to be split up from it etc. – user83455 Dec 04 '21 at 13:24
  • @Fureeish great, it sounds like a namespace is exactly what I need. – user83455 Dec 04 '21 at 13:24
  • @user83455 It's not related to inlining (though a functor should be easier to inline, yes). It's just that `sizeof` of a (stateless) functor is 1 (and since it's [empty](https://en.cppreference.com/w/cpp/types/is_empty), `std::set` will put it on top of its other members, making the size effectively 0), while `sizeof(&MyClass::compare)` is 4 or 8. – HolyBlackCat Dec 04 '21 at 13:55
  • Thank you @HolyBlackCat . I don't really understand but I think that is my problem rather than anything about your answer. I moved my confusion to a new question https://stackoverflow.com/questions/70226584/why-should-i-prefer-a-separate-function-over-a-static-method-for-functional-prog so it can allow a proper answer. I guess it is that one version is compile time / type related (zero size) and the other is runtime / pointer related maybe. – user83455 Dec 04 '21 at 14:42