2015-08-31 51 views
3

我想創建一個shared_ptr內容比較函數來代替關聯容器和std算法中的std::less<T>。我見過使用以下(或類似)模型的自定義比較的幾個例子:我應該擴展std :: less作爲比較函數嗎?

template <typename T> 
struct SharedPtrContentsLess { 
    bool operator()(const boost::shared_ptr<T>& lhs, 
     const boost::shared_ptr<T> rhs) const { 
    return std::less<T>(*lhs, *rhs); 
    //or: return (*lhs) < (*rhs); 
    } 
    //defining these here instead of using std::binary_functor (C++11 deprecated) 
    typedef boost::shared_ptr<T> first_argument_type; 
    typedef boost::shared_ptr<T> second_argument_type; 
    typedef bool return_type; 
}; 

但是,爲什麼不是我想要的,而不是延長std::less?像這樣:

template <typename T> 
struct SharedPtrContentsLess : public std::less< boost:shared_ptr<T> > { 
    bool operator()(const boost::shared_ptr<T>& lhs, 
     const boost::shared_ptr<T> rhs) const { 
    return std::less<T>(*lhs, *rhs); 
    } 
}; 

這會給我買什麼嗎?

我認爲這會讓我免費獲得typedef s,好像我在擴展已棄用的std::binary_function。在C++ 03中,我實際上延伸到std::less。然而,這也將是從C++ 03移植到C++ 11/14和甚至C++ 17時std::binary_function將被刪除,因爲它只是在如下的std::less變化。

我讀過StackOverflow關於std::less的使用,自定義比較函數,甚至一些標準規格和建議的一堆答案。我看到的std::less和指導專業化延長STL容器,但我似乎無法找到擴大反對它std::less或指導的任何實例。我錯過了一個明顯的理由不這樣做?

編輯:刪除C++ 11標記,因爲它是造成答覆者混淆。我希望獲得可移植性,但C++ 03是必需的。如果您提供了一個僅供C++ 11使用的答案(完全正確),請注意。

+0

您的代碼是否真的要在C++ 03編譯器中編譯?這值得額外的工作嗎? – Yakk

+0

@Yakk:是的,目前我們使用的是GCC 4.4.7,它對C++ 11的支持非常有限。我們想要更新,但是我們還沒有這樣做的複雜原因,包括安全自我認證要求和支持程序,這些程序現在都停留在舊編譯器中。儘管這裏額外的工作是什麼?第一個版本是C++ 11版本的樣子,不是嗎? – BloodGain

+0

@Yakk:我想我明白你的意思了。我不認爲第二個版本是更多的工作。事實上,它看起來工作較少,意圖更清晰,這是我們在代碼中所渴望的東西(即優雅)。它看起來好像更少的代碼和更習慣的C++(在我看來)。如果您認爲第一個版本在兩種標準中都更可取,那麼請詳細說明。畢竟,我就是這麼問的! – BloodGain

回答

1

您可以通過簡單的呼叫轉發到到std ::小於或任何其他可比較的對象來創建對任何dereferencable對象(即任何(智能)指針)可重複使用的模板。

// c++11 
template<template<class> Op, class T> struct deref_mixin; 

template<template<class> Op, class T> 
struct deref_mixin { 
    auto operator()(const T &l, const T &r) const 
    -> decltype(std::declval<Op<T>>()(*l, *r)) { 
     return Op<T>{}(*l, *r); 
    } 
}; 

template<template<class> Op> 
struct deref_mixin<Op, void> { 
    template<class T, class U> 
    auto operator()(const T &l, const U &r) const 
    -> decltype(std::declval<Op<T>>()(*l, *r)) { 
     return Op<void>{}(*l, *r); 
    } 
}; 

template<class T> using less_deref = deref_mixin<std::less, T>; 
template<class T> using greater_deref = deref_mixin<std::greater, T>; 
template<class T> using my_comparator_deref = deref_mixin<my_comparator, T>; 

// c++03 
template<template<class> Op, class T> 
struct deref_mixin { 
    bool operator()(const T &l, const T &r) const { 
     Op<T> op; 
     return op(*l, *r); 
    } 
}; 
// Technically, the void template partial specialization isn't defined in c++03, but it should have been :) 
template<template<class> Op> 
struct deref_mixin<Op, void> { 
    template<class T, class U> 
    bool operator()(const T &l, const U &r) const { 
     Op<void> op; 
     return op(*l, *r); 
    } 
}; 

template<class T> struct less_deref : deref_mixin<std::less, T> {}; 
+0

我剛剛在其中一條評論中注意到,您對C++ 11代碼的訪問權限有限。你可以擺脫返回類型的決定,並使用布爾。這應該滿足任何謂詞用例。 – Andrew

+0

我決定將此答案標記爲已接受,因爲它顯示了一個更好的通用方法來執行我想要的操作。我認爲這個問題的實際答案出自Yakk的評論之一(切片的危險)。如果有人決定擴大這一點,我會將其標記爲對問題的最直接回答,但我認爲這和雅克的答案提供了最好的指導。這個有一個C++ 03版本,所以它得到了推動。 – BloodGain

1

正如你在你的問題中所說,如果你從std::less繼承,你會得到std::less中的三個typedef。我最喜歡從它繼承的是它描述你的意圖。當我看到

struct some_non_specific_name : std::less<some_type> 

我知道在那裏,這是要表現爲一個<some_type函子。我不必讀取結構體來找出任何東西。

1

據我所看到的,你都不會錯過任何缺點。正如你所提到的,你會自動獲得typedefoperator<必須在兩種情況下定義,並且其實施沒有區別。

有一件事,你可能會得到,你可能會發現整齊,壞的或只是沒有適用於您的用例(從herehere):有一種std::lessstd::less<void>專業化已模板operator<推斷返回類型給定參數的operator<

除非你打算使用SharedPtrContentsLess<void>(這可能是沒有意義的話),這兩種解決方案是等效的。

1

我會寫一個deref_less。首先,my_less是巧妙地調用std::less

struct my_less { 
    template<class Lhs, class Rhs, 
    class R = std::result_of_t< std::less<>(Lhs const&, Rhs const&) > 
    // class R = decltype(std::declval<Lhs const&>() < std::declval<Rhs const&>()) 
    > 
    R operator()(Lhs const&lhs, Rhs const&rhs)const{ 
    return std::less<>{}(lhs, rhs); // or lhs<rhs 
    } 
    // exact same type uses `std::less<T>`: 
    template<class T, 
    class R = std::result_of_t< std::less<>(T const&, T const&) > 
    > 
    R operator()(T const& lhs, T const& rhs)const{ 
    return std::less<T>{}(lhs, rhs); 
    } 
    template<class Lhs, class Rhs, 
    std::enable_if_t< std::is_base_of<Lhs, Rhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr 
    > 
    bool operator()(Lhs const* lhs, Rhs const* rhs)const{ 
    return std::less<Lhs const*>{}(lhs, rhs); 
    } 
    template<class Lhs, class Rhs, 
    std::enable_if_t< std::is_base_of<Rhs, Lhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr 
    > 
    bool operator()(Lhs const* lhs, Rhs const* rhs)const{ 
    return std::less<Rhs const*>{}(lhs, rhs); 
    } 
    template<class Lhs, class Rhs, 
    std::enable_if_t< 
     !std::is_base_of<Rhs, Lhs>{} 
     && !std::is_base_of<Lhs, Rhs>{} 
     && !std::is_same<Lhs, Rhs>{} 
    >* = nullptr 
    > 
    bool operator()(Lhs const* lhs, Rhs const* rhs)const = delete; 
}; 

然後,一個deref_less,做了*然後調用myless

struct deref_less { 
    template<class Lhs, class Rhs, 
    class R = std::result_of_t< my_less(decltype(*std::declval<Lhs>()), decltype(*std::declval<Rhs>())) > 
    > 
    R operator()(Lhs const& lhs, Rhs const&rhs)const { 
    return my_less{}(*lhs, *rhs); 
    } 
}; 
在C++ 14

,但我用一切都是易於更換(std::less<>可以用例如decltype和<來代替)。

+0

我還沒有完全理解這個答案,但是頂部的所有東西('my_less')都是爲了解決我的兩個版本中的切片問題?有沒有在C++ 03中解決這個問題的好方法? – BloodGain

+1

@BloodGain不是切片,而是指針上的'<'表現不佳。在相關的指針上,我使用上面的'std :: less'使其表現良好。當我發佈這個時,你的問題是[標籤:C++ 11]。從那以後,你已經明確表示你缺少一個C++ 11編譯器,所以這個答案相對過時了。 – Yakk

+0

是的,我添加了C++ 11標籤,因爲我希望這是可移植的。我現在看到它引入了太多的困惑,所以我刪除了它。問題上下文詢問前向可移植性,因此應該足夠了。謝謝! – BloodGain

相關問題