0

The following code snippet aims to store all points in the member function mainFunc of enclosing class Solution in a priority_queue (i.e. pq), such that all points are in the order according to their distance to origin. However, the compiler reports an error:

error: invalid use of non-static data member 'Solution::ori'

Then I change the 3rd line of Point ori to static Point ori and change ori to Solution::ori in the function of distance(Point p), the link error occurs:

undefined reference to 'Solution::ori'

Could anyone help me on this? Thanks in advance!

/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */

class Solution {
private:
    Point ori;
    class Comparator {
    public:
        // calculate the euclidean distance between p and ori
        int distance(Point p) {
            return pow(p.x-ori.x, 2) + pow(p.y-ori.y, 2);
        }
        // overload the comparator (), the nearest point to 
        // origin comes to the first
        bool operator() (Point l, Point r) {
            if (distance(l) > distance(r)) {
                return true;
            }
        }        
    };

public:
    /*
     * @param points: a list of points
     * @param origin: a point
     */
    void mainFunc(vector<Point> points, Point origin) {
        ori = origin;
        priority_queue<Point, vector<Point>, Comparator> pq;
        for (int i = 0; i < points.size(); i++) {
            pq.push(points[i]);
        }
    }
};
ROBOT AI
  • 1,217
  • 3
  • 16
  • 27
  • Sorry if I'm completely missing something, but why do you have the `public:` modifier in the Solution class twice? – Zexus Dec 24 '17 at 08:06
  • Sorry, that's a typo. I have corrected the first one to private. But the errors remains the same. – ROBOT AI Dec 24 '17 at 08:07
  • @ROBOTAI `Solution` and `Comparator` are completely distinct classes besides that `Comparator` is declared as a local type used within `Solution`. None of them has _direct access_ to the members of each other. – user0042 Dec 24 '17 at 08:08
  • @user0042 I see. Then how should we modify the codes to make the `Comparator` get the value of `ori`? – ROBOT AI Dec 24 '17 at 08:11
  • @ROBOTAI I'd ditch the `Comparator` completely and use a lambda function within the `Solution::mainFunc()`. Bit of broad to describe here. A `lambda expression` has the advantage, that it's framed within it's declaration context. – user0042 Dec 24 '17 at 08:17
  • @user0042 That is one way. But what if we have to use a `Comparator` class as compare function? – ROBOT AI Dec 24 '17 at 08:19
  • @ROBOTAI Why do you have to use one? Pre c++11? Because the teacher requires you to do? – user0042 Dec 24 '17 at 08:20
  • @ROBOTAI There's a number of different approaches you can use: https://stackoverflow.com/questions/1380463/sorting-a-vector-of-custom-objects – user0042 Dec 24 '17 at 08:27
  • If you make ori static, you also need to instantiate it in your .cpp like so: Point Solution::ori; – super Dec 24 '17 at 08:29
  • @super, thanks for the reply! yes, it has to be initialized outside the class. Is there any way to pass the value of `origin` to `Comparator`? – ROBOT AI Dec 24 '17 at 08:32
  • @ROBOTAI _"Is there any way to pass the value of `origin` to `Comparator`?"_ Sure. Pass either a reference to `Solution` or the value directly to `Comparator` in its constructor. – user0042 Dec 24 '17 at 08:38
  • @ROBOTAI Yet another option is to make ori a static member of Comparator instead of Solution. Then you can set it as you already do in your mainFunc. – super Dec 24 '17 at 08:42
  • @super I ever tried. And use `Comparator::ori = origin` in `mainFunc`. But there is link error: undefined reference to `Solution::Comparator::ori'. – ROBOT AI Dec 24 '17 at 08:46
  • @user0042 Thanks for the reply! Since the interface of `Solution` is fixed so I cannot change its constructor. Also passing `origin` to constructor of `Comparator` may not be working, since the `Comparator` is used as class (not instance) in priority_queue. I have to implement in current way in order to refactor codes. – ROBOT AI Dec 24 '17 at 08:49
  • @ROBOTAI Add `Point Solution::Comparator::ori;` to your .cpp file. – super Dec 24 '17 at 08:50
  • @ROBOTAI No need to change `Solution`s constructor, you misundersood something. You've seen the various [`std::priority_queue` constructor options](http://en.cppreference.com/w/cpp/container/priority_queue/priority_queue) which essentially allow to pass an instance of `Comparator` to the queue instance, did you? So what's the problem actually? – user0042 Dec 24 '17 at 08:53
  • @super, yes, that's one way. Add the declaration: `Point Solution::Comparator::ori;` to outside of `Solution` also works. But can we reach the goal by writing everything in the class of `Solution`? – ROBOT AI Dec 24 '17 at 08:54
  • @user0042 That may works. Could you paste the modification in answer, instead of comments? Thanks! – ROBOT AI Dec 24 '17 at 09:01
  • If you add if outside of Solutions you will get an error if you try to include that header file in more then 1 place. You need to put the instantiation in your .cpp file or use C++17 `inline variable` if you want to have it in the header file. – super Dec 24 '17 at 09:02
  • Thanks for telling me the new property in C++17 @super. I still want to put all things in `Solution`. – ROBOT AI Dec 24 '17 at 09:04

1 Answers1

2

You can modify your Comparator declaration to take a certain Point value in it's constructor:

class Solution {
private:
    Point ori;
    class Comparator {
    public:
        // Save the value in the functor class
        Comparator(const Point& origin) : origin_(origin) {}

        // calculate the euclidean distance between p and origin
        int distance(Point p) {
            return pow(p.x-origin_.x, 2) + pow(p.y-origin_.y, 2);
        }
        // overload the comparator (), the nearest point to 
        // origin comes to the first
        bool operator() (Point l, Point r) {
            if (distance(l) > distance(r)) {
                return true;
            }
        }        
    private:
         Point origin_;
    };

public:
    /*
     * @param points: a list of points
     * @param origin: a point
     */
    void mainFunc(vector<Point> points, Point origin) {
        ori = origin;
        priority_queue<Point, vector<Point>> pq(Comparator(ori));
                                             // ^^^^^^^^^^^^^^^ 
                                             // Pass an instance of 
                                             // the functor class
        for (int i = 0; i < points.size(); i++) {
            pq.push(points[i]);
        }
    }
};
user0042
  • 7,917
  • 3
  • 24
  • 39
  • Yes, it works. Thanks! The constructor of pq is a little tricky :) – ROBOT AI Dec 24 '17 at 09:12
  • Besides, the accepted answer has a bug. Following codes work: `const Comparator cmp(origin); priority_queue, Comparator> pq(cmp);` The input for pq constructor has to be `const` type. – ROBOT AI Dec 24 '17 at 09:20