2016-04-29 15 views
1

沒有多餘的代碼,讓我們看看下面的代碼示例:排序基於將吸氣構件功能的STL容器,以書面

// Example program 
#include <iostream> 
#include <string> 
#include <algorithm> 

class A 
{ 
public: 
    A(const std::string& name) : name(name) {} 

    inline const std::string& getName() const { return name; } 

private: 
    std::string name; 
}; 

int main() 
{ 
    std::vector<A> v; 
    v.push_back(A("b")); 
    v.push_back(A("a")); 

    // want to sort the container, based on it's name! 
} 

我知道如何做到這一點(無論是定義一個內部或外部operator<,或宣佈一個仿函數結構/類,它提供一個operator(),並把它傳遞給std::sort fonction),例如:

bool operator<(const A& a) const 
{ 
    return getName() < a.getName(); 
} 

但是,我很懶有這個我想基於一個包含的類進行排序的容器都必須這樣做屬性,特別是當t他的課爲它提供了一個getter功能。

是否有可能基於類成員函數結果值請求排序(如果此函數顯然有比較器可用)?

喜歡的東西:

std::sort(v.begin(), v.end(), &(A::getName)); 

無需聲明一個新的運營商或仿函數?

可選問題:如果vector包含指針(std::vector<A*> v)......如果單行語句可以根據A::getName結果對它進行排序,那該怎麼辦?

+0

範圍TS應該允許'的std ::排序(V,&A ::的getName);'。 – Jarod42

+0

這將是偉大的....但它報告非法使用非靜態成員函數const string&A :: getName()const''(參見http://cpp.sh/7eqf) – jpo38

+0

如果使用C++ 11或更大,你可以使用lambda:'std :: sort(v.begin(),v.end(),[](const A&a,const A&b){return a.getName() Garf365

回答

7

您可以在使用lambda函數來排序你的載體:

std::sort(v.begin(), v.end(), [] (A const& a, A const& b) { 
     return a.getName() < b.getName(); 
    }); 

它比你的版本要長,但你並不需要寫之前的sort調用任何東西。

如果你需要做的那種東西的時候,你可以把一切都放在一個小函子(並允許它兩個AA*和智能指針工作),它使用mem_fn(感謝@TC):

template <typename T, typename R> 
struct cmp_attr { 

    using fun_t = R (T::*)() const; 

    cmp_attr (fun_t g) : _g(g) { } 

    template <typename U> 
    bool operator() (U const& a, U const& b) const { 
     auto fn = std::mem_fn(_g); 
     return fn(a) < fn(b); 
    } 

private: 
    fun_t _g; 
}; 

// Utility function to have template parameters deduced, a like std::make_pair 
template<typename T, typename R> 
auto make_cmp_attr (R (T::*g)() const) { 
    return cmp_attr<T, R>(g); 
} 

然後:

struct A { const std::string& getName(); } 
struct B: public A { } 

std::vector<A> v1; // vector of A 
std::sort(v1.begin(), v1.end(), make_cmp_attr(&A::getName)); 
std::vector<A*> v2; // vector of A* 
std::sort(v2.begin(), v2.end(), make_cmp_attr(&A::getName)); 
std::vector<B> v3; // vector of child class 
std::sort(v3.begin(), v3.end(), make_cmp_attr(&A::getName)); 
std::vector<B*> v4; // vector of pointer of child class 
std::sort(v4.begin(), v4.end(), make_cmp_attr(&A::getName)); 
+0

你可以編寫一個函數來同時允許'a。* cmp'或'a - > * cmp'。 – Jarod42

+0

@ Jarod42 true,答案已更新。 – Holt

+0

如果真的是通用的(處理任何智能指針)會更復雜一些,並且需要SFINAE。但對於OP應該足夠了。 – Jarod42

1

我建議你不超載operator<只是因爲你心裏有一個特定用例。將排序邏輯保持在您需要的地方;它不是該類型的一部分。

如果你想預測定期進行排序,它可能是值得寫一個小幫手:

template <typename C, typename R> 
struct SortByPred 
{ 
    using P = R (C::*)() const; 

    P const p_; 
    SortByPred(P p) : p_(p) {} 

    template <typename T> 
    bool operator()(T const & lhs, T const & rhs) const 
    { 
     return (lhs.*p_)() < (rhs.*p_)(); 
    } 
}; 

template <typename C, typename R> 
SortByPred<C, R> SortBy(R (C::*p)() const) 
{ 
    return SortByPred<C, R>(p); 
} 

用法:

std::sort(v.begin(), v.end(), SortBy(&A::getName)); 
+0

[Demo](https://ideone.com/UZZnxe) –

+0

您甚至可以編寫運算符以允許'a。* p_'或'a - > * p_'。 – Jarod42

+0

@KerrekSB _「...它不是類型的一部分」_ - 它可以是。在頻譜的另一端,有內建類型,例如'int',它們本質上可以用'<'排序。中間的某處是類似於「std :: string」的類型,默認情況下使用「字符串排序」進行排序,但也取決於語義)_could_按長度排序甚至是(古老的,我知道,但仍...)容量。隨着你走向另一端,從分類行爲中分離出來的類型變得更加重要,我想。 –