John Yang
John Yang

Reputation: 577

Sorting a std::vector with a compare functor which has access to class members

I want to sort out a vector using std::sort with self-defined comparison function/functor. Inside this function I always want to have access functions or variables defined within the class.

Class MainClass
{
    protected: // Variables
        float fixedpoint;

    protected: // Methods
        float DistanceBetweenTwoPoints(float,float);
        void function1();

    struct CompareDistanceToGoal : public std::binary_function<float,float,bool>
    {
        bool operator()(float p1, float p2)
        {
            // return ( p1 < p2);
            // I want to use the following code instead of the above
            return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
        }
    };
}

Inside function1:

void MainClass::function1()
{
    std::vector<float> V1;
    std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal());
}

So instead of using "return (p1 < p2)", I want to have access to fixedpoint and maybe DistanceBetweenTwoPoints() function. Is this possible (i.e. using friend identifier somehow)?

Can anybody show me how to do this? Thanks.

Upvotes: 1

Views: 1655

Answers (3)

Mike Seymour
Mike Seymour

Reputation: 254501

As a nested type, CompareDistanceToGoal has access to all members of MainClass; there's no need to declare it a friend. (Although this is a moderately recent change to the language; a compiler that doesn't implement C++11 might need a friend declaration, friend CompareDistanceToGoal;, to match modern behaviour).

However, since these members are non-static, you can't do anything with those members unless you provide a MainClass object. Perhaps you want to make them static; or perhaps you want to "capture" an object:

struct CompareDistanceToGoal // no need for that binary_function nonsense
{
    MainClass & mc;

    CompareDistanceToGoal(MainClass & mc) : mc(mc) {}

    bool operator()(float p1, float p2)
    {
        return mc.DistanceBetweenTwoPoints(mc.fixedpoint,p1) < 
               mc.DistanceBetweenTwoPoints(mc.fixedpoint,p2);
    }
};

std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal(some_main_class_object));

Upvotes: 3

Tony Delroy
Tony Delroy

Reputation: 106126

It's a bit hard to know what you're trying to do, but this seems to have as much chance of being what you want as anything else... note that MainClass stores a fixedpoint then provides a functor (no need for a nested class) which is then used by sort. In the code below, it sorts the vector so elements closest to the MainClass fixedpoint are earlier in the vector. See it running at ideone.com.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

class MainClass
{
  public:
    MainClass(float fixedpoint) : fixedpoint_(fixedpoint) { }
    bool operator()(float p1, float p2) const
    {
        float d1 = DistanceBetweenTwoPoints(fixedpoint_,p1);
        float d2 = DistanceBetweenTwoPoints(fixedpoint_,p2);
        return d1 < d2 || d1 == d2 && p1 < p2;
    };

  protected: // Variables
    float fixedpoint_;
    static float DistanceBetweenTwoPoints(float a,float b) { return std::fabs(a - b); }
    void function1();
};

int main()
{
    std::vector<float> v { 1, 3, 4.5, 2.3, 9, 12 };
    std::sort(std::begin(v), std::end(v), MainClass(9.3));
    for (auto f : v)
        std::cout << f << '\n';
}

Upvotes: 1

Bryan Chen
Bryan Chen

Reputation: 46598

you can capture value manually

struct CompareDistanceToGoal : public std::binary_function<float,float,bool>
{
    float fixedpoint;

    CompareDistanceToGoal(float p) : fixedpoint(p) {}
    bool operator()(float p1, float p2)
    {
        return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
    }
};

and use it

void MainClass::function1()
{
    std::vector<float> V1;
    std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal(fixedpoint));
}

or if C++11 is available, use lambda to capture value

void MainClass::function1()
{
    std::vector<float> V1;
    std::sort(V1.begin(),V1.end(),[=](float p1, float p2){
        return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
    });
}

Upvotes: -1

Related Questions