【问题标题】:Template class dealing with values and reference semantics处理值和引用语义的模板类
【发布时间】:2014-01-06 15:18:44
【问题描述】:

我一直在使用二进制堆处理优先级队列,并为此开发了一个类,如下所示。

#include <iostream>
#include <type_traits>

template<class T, int N>
class BinaryHeap{

    template<class T1>
    class has_less_than_operator
    {
    private:
        class no{};
        template<class X>
        static auto has(X&& t) -> decltype (t.operator < (t));

        static no has(...);
    public:
        enum {
            value =  !std::is_same<
            decltype(has( std::declval<T1>() )),
            no>::value
        };
    };

    static_assert(std::is_copy_assignable<T>::value &&
                  std::is_copy_constructible<T>::value,
                  "Must be copy assignable and constructable");
public:
    BinaryHeap() : used_(0){

    }

    BinaryHeap(BinaryHeap const& other) = default;

    BinaryHeap& operator = (BinaryHeap const& other) = default;

    inline T& max(){
        return elements_[FIRST];
    }

    inline T const & max() const{
        return elements_[FIRST];
    }

    void insert(T const& item){
        elements_[++used_] = item;
        swim(used_);
    }

    inline bool full() const{
        return used_ == N;
    }

    void deleteMax(){
        std::swap(elements_[used_],elements_[FIRST]);
        sink(FIRST);
        elements_[used_--] = T();
    }

private:

    template<class T1>
    class has_dereference_operator
    {
    private:
        class no{};
        template<class X>
        static auto has(X&& t) -> decltype (t.operator * ());

        static no has(...);
    public:
        enum {
            value =  !std::is_same<
            decltype(has( std::declval<T1>() )),
            no>::value
        };
    };



    inline bool parent_less(int position,std::integral_constant<int,0> i){
        return elements_[ position / 2] < elements_[position];
    }

    inline bool parent_less(int position,std::integral_constant<int,1> i){
        return *(elements_[ position / 2]) < *(elements_[position]);
    }

    void swim(int position){
        while(position > 1 && parent_less(position,std::integral_constant<int, has_dereference_operator<T>::value>()))
        {
            std::swap(elements_[ position / 2], elements_[position]);
            position /= 2;
        }
    }

    inline int which_less(int p1, int p2, std::integral_constant<int,0> i){
        return (elements_[ p1] < elements_[p2]) ? p1 : p2;
    }

    inline int which_less(int p1, int p2, std::integral_constant<int,1> i){
        return (*(elements_[ p1]) < *(elements_[p2])) ? p1 : p2;
    }

    inline int which_greater(int p1, int p2, std::integral_constant<int,0> i){
        return (elements_[ p1] < elements_[p2]) ? p2 : p1;
    }

    inline int which_greater(int p1, int p2, std::integral_constant<int,1> i){
        return (*(elements_[ p1]) < *(elements_[p2])) ? p2 : p1;
    }

    void sink(int position){
        while(position * 2 <= used_){
            int first = position * 2;
            if(first > used_) break;

            int greater_child = which_greater(first, first + 1, std::integral_constant<int, has_dereference_operator<T>::value>());
            int lesser = which_less(greater_child, position, std::integral_constant<int, has_dereference_operator<T>::value>());
            if(lesser == greater_child)
                break;

            std::swap(elements_[greater_child], elements_[position]);
            position = greater_child;
        }
    }

    inline int current_position() const{
        return used_ + 1;
    }

    static const int MAX = N + 1;
    static const int FIRST = 1;
    static const int LAST = N;
    T elements_[MAX];
    int used_;
};

int main(int argc, const char * argv[])
{

    BinaryHeap<int, 10> b;

    b.insert(1);
    b.insert(20);
    b.insert(21);
    b.insert(3);
    b.insert(2);

    std::cout << "Max: " << b.max() << std::endl;

    b.deleteMax();

    std::cout << "Max: " << b.max() << std::endl;

    return 0;
}

虽然我有这个工作,但我需要处理比较指针/共享指针说使用解引用运算符和值只是按原样使用它们的差异。我目前正在使用 SFINAE 根据该类是否具有运算符 * 来执行此操作。

这是实现这一目标的正确方法吗?

布莱尔

【问题讨论】:

  • 我不确定为什么在有std::priority_queue 时实现优先级队列,在有std::make_heap 等时实现二进制堆。除此之外,为什么要根据提供的类型更改行为?您可以改用包装器或谓词。
  • 这实际上不是重点。它是一种学习练习。我知道 std::priority_queue。
  • 惯用的交换方式是using std::swap; swap(foo, bar);。这将调用特定类型的swap(T&amp;, T&amp;)(如果存在),如果不存在则回退到std::swap
  • 您的 has_foo 特征不适用于非成员运算符重载。不是检查成员的类型,而是检查表达式的结果类型,例如,decltype(t &lt; t)decltype(*t)

标签: c++ templates sfinae compile-time


【解决方案1】:

使用这样的启发式方法的问题在于,代码的客户端并不总是希望您执行此操作,并且您没有提供改变行为的方法。有一天,客户可能希望使用您的类来存储指针并实际使用std::less&lt;T&gt; 对它们进行排序,而不是取消引用(例如BinaryHeap&lt;void*,32&gt;)。即使使用非指针,客户端也可能只是想要一个不同于&lt; 强加的排序。

当标准库需要执行比较时,它通常默认使用std::less&lt;T&gt;,但为客户端提供了一种覆盖该选择的方法(例如std::priority_queuestd::sort)。如果我正在编写您的课程,我将通过默认为std::less&lt;T&gt; 的比较运算符对其进行参数化,就像标准库一样。我还将提供一个方便的取消引用比较器模板,以方便客户使用指针按指针进行排序。

【讨论】:

  • 您对函数模板的同一问题有何看法?
猜你喜欢
  • 2016-01-04
  • 2013-07-16
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多