处理值和引用语义的模板类

时间:2014-01-06 15:18:44

标签: c++ templates sfinae compile-time

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

#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来执行此操作,如果该类具有运算符*。

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

布莱尔

1 个答案:

答案 0 :(得分:2)

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

当标准库需要执行比较时,它通常默认使用std::less<T>,但为客户提供了覆盖该选择的方法(例如std::priority_queuestd::sort)。如果我正在编写你的类,我会通过比较运算符默认参数化std::less<T>,就像标准库那样。我还提供了一个方便的解除引用比较器模板,使客户可以轻松地使用指针进行订购。