2016-12-13 62 views
3

我寫了一個PointCollection類。如何通過一個比較器正確的STL功能

PointCollection持有一堆點。

它有2個成員函數。

addPoint(Point point)

findNearestKPoints(Point center, int k)

每次findNearestKPoints被調用時,一個center被指定爲得到k它周圍的最近點。

但它不能編譯:

error: called object type 'bool (PointCollection::*)(const Point &, const Point &) const' is not a function or function pointer

如何正確做呢?

我的代碼是波紋管作爲參考:

struct Point { 
    int val_; 
    Point() {} 
    Point(int val) : val_(val) {} 
}; 

class PointCollection { 
private: 
    vector<Point> points_; 
    Point center_; 
public: 
    PointCollection() {} 
    virtual ~PointCollection() {} 

    void addPoint(const Point &point) { 
     points_.push_back(point); 
    } 

    bool compare(const Point &a, const Point &b) const { 
     return std::abs(a.val_ - center_.val_) < std::abs(b.val_ - center_.val_); 
    } 

    vector<Point> findNearestKPoints(Point center, int k) { 
     center_ = center; 

     nth_element(points_.begin(), points_.begin() + k - 1, points_.end(), 
        &PointCollection::compare); 

     return vector<Point>(points_.begin(), points_.begin() + k); 
    } 
}; 
+0

你寫的'比較'函數需要一個工作實例,它不需要查看該函數的內容。使其成爲'靜態',或使其成爲非會員'朋友'功能。 – Chad

+0

@Chad:該函數使用成員'center_',所以它是必要的。 –

+0

@BenjaminLindley呃---是的。這讓我覺得這只是_wrong_。二進制比較函數幾乎總是比較傳入的對象,並且不會嘗試進行第三次比較......我不太瞭解這個特定問題域是否可以說出是否錯誤。 – Chad

回答

8

比較器是一個可調用對象。換句話說:一個函數指針或一個lambda閉包,或者一個帶有合適的operator()的類。

&PointCollection::compare不是可調用的對象。這是一種類方法。這不是一個可調用的對象,因爲你不能直接調用它。類方法只能在的實例上調用。你必須在某個地方有這個類的實例,並且你調用它的方法compare()。它看起來像一個功能,但它確實不是。這是一種類方法。

一個簡單的解決辦法是通過一個lambda捕獲this,像(C++ 14):

nth_element(points_.begin(), points_.begin() + k - 1, points_.end(), 
       [this](const auto &a, const auto &b) 
        { 
         return this->compare(a, b); 
        }); 

拉姆達捕獲this,和compare()可以this被調用,就如同它可以是直接從父方法調用。

P.S.您的findNearestKPoints()正在返回vector<Point>,而不是vector<int>,正如您聲明的那樣。

+1

如果C++ 14不是一個選項,來自C++ 11的std :: bind也可能有幫助(如果有人絆倒apon,並且不願/無法使用C++ 14,那麼只需要注意) – Creris

+2

那些人們也可以將'auto'改爲具體類型(在這種情況下爲'Point')。 –

+0

謝謝。這裏將'auto'改爲'Point'就行。 –

1

center是一個臨時變量,不需要將其作爲成員變量存儲。

最終版本:

class PointCollection { 
private: 
    vector<Point> points_; 
public: 
    PointCollection(vector<int> nums) { 
     for (int num : nums) { 
      points_.push_back({num}); 
     } 
    } 
    virtual ~PointCollection() {} 

    void addPoint(const Point &point) { 
     points_.push_back(point); 
    } 

    vector<Point> findNearestKPoints(Point center, int k) { 
     nth_element(points_.begin(), points_.begin() + k - 1, points_.end(), 
        [&center] (const Point &a, const Point &b) { 
         return compare(a, b, center); 
        }); 

     return vector<Point>(points_.begin(), points_.begin() + k); 
    } 

    static bool compare(const Point &a, const Point &b, Point center) { 
     return std::abs(a.val_ - center.val_) < std::abs(b.val_ - center.val_); 
    } 
}; 
+0

這也適用,雖然這種方法在捕獲對象很大時會變得效率低下,因爲它是通過值來捕獲的,所以它必須被複制構建如果是這種情況,只需參考它。 –

1

函子的解決方案是優雅的,以及,完成的圖片。

你可以在裏面(或外部)添加此仿函數的PointCollection類:

struct compare { 
    Point center_; 
    bool operator()(const Point &a, const Point &b) const { 
     return std::abs(a.val_ - center_.val_) < std::abs(b.val_ - center_.val_); 
    } 
} 

然後:

std::nth_element(points_.begin(), points_.begin() + k - 1, points_.end(), compare{center}); 

優勢拉姆達:更簡單,更方便的實例比較(仿函數)的地方你想使用它,而lambda需要重新寫一遍(想象你還需要編寫其他需要使用比較器的方法,即最遠的K等......)