1

I want to use the function nth_element with my own sorting function (which should have access to the data of the object) within a class. Currently, I am doing the following:

class Foo
{
 public:
   glm::vec3 *points;
   int nmbPoints;

   bool idxPointCompareX(int a, int b);
   void bar();
}

bool Foo::idxPointCompareX(int a, int b)
{return points[a].x < points[b].x;)

void Foo::bar()
{
   stl::vector<int> idxPointList;
   for(int i = 0; i < nmbPoints; i++) idxPointList.push_back(i);  

   stl::nth_element(idxPointList.first(),idxPointList.first()+nmbPoints/2,idxPointList.end(), idxPointCompareX);
}

Of course, this did not work and I got the error: "reference to non-static member function must be called". After that, I had a look at Reference to non-static member function must be called, How to initialize std::function with a member-function? and some other questions on here. I understand why this did not work, but I'm unsure how to solve this.

Can someone help me and tell me how to solve this problem?

Community
  • 1
  • 1
DanceIgel
  • 646
  • 7
  • 24

3 Answers3

6

To take the address of a member function you need to use the correct syntax, i.e. &Foo::idxPointCompareX not just idxPointCompareX

But you also need a Foo object to call the function on, so you would need to bind one to it. Presumably you mean to call it on this so you could use std::bind:

using namespace std::placeholders;
stl::nth_element(begin, begin+n, end,
                 std::bind(&Foo::idxPointCompareX, this, _1, _2));

Or simpler, use a lambda function:

stl::nth_element(begin, begin+n, end, 
                 [this](int a, int b) { return idxPointCompareX(a, b);}
);

This creates a lambda function that captures this and passes its arguments on to the idxPointCompareX function on the captured this pointer.

Jonathan Wakely
  • 153,269
  • 21
  • 303
  • 482
  • As I mentioned already under his answer, I made a mistake while writing the question. idxPointCompareX uses an index to get access to the point data of 'points'. – DanceIgel Nov 16 '15 at 09:42
  • you're missing n'th element here. `stl::nth_element(begin, begin + smth, end, [this](int a, int b) { return idxPointCompareX(a, b);} );` – Алексей Неудачин Jul 30 '20 at 18:36
2

idxPointCompareX is a member function, i.e. it cannot be called without some reference to a Foo object. Looking at its definition though, it seems that it doesn't need to be a member since it's purely defined in terms of its arguments.

You could make it a static function (i.e. a "class function") or a free function and then pass that to std::nth_element.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
Frerich Raabe
  • 81,733
  • 18
  • 105
  • 196
0

You can't call an object method before it is created, so you have some options here:

  1. Make the method static

  2. Leave the constructor empty and move all into an init method where you call the compare part

  3. Use a lambda

Examples:

Static method:

static bool idxPointCompareX(glm::vec3 a, glm::vec3 b)
{return a.x < b.x;)

Init method:

Foo::bar()
{
   stl::vector<int> idxPointList;
   for (int i = 0; i < nmbPoints; i++)
       idxPointList.push_back(i);  
}

Foo::init()
{
    stl::nth_element(idxPointList.first(),
                     idxPointList.first()+nmbPoints/2,idxPointList.end(),
                     idxPointCompareX);
}

Lambda:

Foo::bar()
{
   stl::vector<int> idxPointList;
   for (int i = 0; i < nmbPoints; i++)
       idxPointList.push_back(i);  

   stl::nth_element(idxPointList.first(),
                    idxPointList.first()+nmbPoints/2,idxPointList.end(),
                    [](int a, int b){return points[a].x < points[b].x;));
}

I would go for the lambda version myself.

Community
  • 1
  • 1
Netwave
  • 23,907
  • 4
  • 31
  • 58