首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >泛型(多)SkipList适配器类

泛型(多)SkipList适配器类
EN

Code Review用户
提问于 2018-10-05 18:32:29
回答 1查看 75关注 0票数 1

今天,我编写了SkipList的第一个实现(在std::vector之上),因为我厌倦了手动使用std::lower_bound和其他函数。我试图尽可能地创建它,以便为用户提供尽可能多的健康自由。在实现过程中,我总是关注std::setstd::multiset文档,以保持接口类似于这些文档。

起初,我决定将非const迭代器隐藏在公共接口中,以防止在意外情况下对SkipList进行操作,但在第一次迭代之后,它的使用非常不优雅。至少,当您尝试使用value_type对象的一个属性作为键时,它显示了它的weaknesses.To,更改了该元素的任何其他变量,我必须先删除它,然后再插入它。这既不好,也不干净,也不优雅;这也是我决定公开非const迭代器的原因。顺便说一句,std::setstd::multiset也公开了它们,因此对于该类的用户来说,这不应该是一个很大的惊喜。

以下是SkipList (唯一元素)和MultiSkipList的代码。

代码语言:javascript
复制
#include 
#include 
#include 

namespace detail
{
    template , class Container = std::vector>
    class BaseSkipList
    {
    public:
        using container_type = Container;
        using value_type = typename Container::value_type;
        using size_type = typename Container::size_type;
        using iterator = typename Container::iterator;
        using reverse_iterator = typename Container::reverse_iterator;
        using const_iterator = typename Container::const_iterator;
        using const_reverse_iterator = typename Container::const_reverse_iterator;

        BaseSkipList() = default;

        explicit BaseSkipList(Compare&& _compare) :
            m_Compare(std::move(_compare))
        {}

        explicit BaseSkipList(Container _container, Compare&& _compare = Compare()) :
            m_Container(std::move(_container)),
            m_Compare(std::move(_compare))
        {
            std::sort(std::begin(m_Container), std::end(m_Container), m_Compare);
        }

        BaseSkipList(const BaseSkipList&) = default;
        BaseSkipList(BaseSkipList&&) = default;
        BaseSkipList& operator =(const BaseSkipList&) = default;
        BaseSkipList& operator =(BaseSkipList&&) = default;

        bool empty() const
        {
            return std::empty(m_Container);
        }

        size_type size() const
        {
            return std::size(m_Container);
        }

        void clear()
        {
            m_Container.clear();
        }

        void reserve(size_type _new_cap)
        {
            m_Container.reserve(_new_cap);
        }

        size_type capacity() const noexcept
        {
            return m_Container.capacity();
        }

        void shrink_to_fit()
        {
            return m_Container.shrink_to_fit();
        }

        template 
        std::pair insert(TValueType&& _value)
        {
            return _insert(begin(), std::forward(_value));
        }

        template 
        void insert(TIterator _itr, const TIterator& _end)
        {
            for (; _itr != _end; ++_itr)
                _insert(*_itr);
        }

        void insert(std::initializer_list _ilist)
        {
            insert(std::begin(_ilist), std::end(_ilist));
        }

        iterator erase(const_iterator _itr)
        {
            return m_Container.erase(_itr);
        }

        iterator erase(const_iterator _first, const_iterator _last)
        {
            return m_Container.erase(_first, _last);
        }

        template 
        iterator erase(const TComparable& _value)
        {
            auto itr = std::lower_bound(std::begin(m_Container), std::end(m_Container), _value, m_Compare);
            if (itr != end())
                return m_Container.erase(itr);
            return end();
        }

        template 
        iterator find(const TComparable& _value)
        {
            return _find(m_Container, m_Compare, _value);
        }

        template 
        const_iterator find(const TComparable& _value) const
        {
            return _find(m_Container, m_Compare, _value);
        }

        template 
        iterator lower_bound(const TComparable& _value)
        {
            return _lower_bound(m_Container, m_Compare, _value);
        }

        template 
        const_iterator lower_bound(const TComparable& _value) const
        {
            return _lower_bound(m_Container, m_Compare, _value);
        }

        template 
        iterator upper_bound(const TComparable& _value)
        {
            return _upper_bound(m_Container, m_Compare, _value);
        }

        template 
        const_iterator upper_bound(const TComparable& _value) const
        {
            return _upper_bound(m_Container, m_Compare, _value);
        }

        template 
        bool contains(const TComparable& _value) const
        {
            return find(_value) != end();
        }

        /*#####
        # multi element stuff
        #####*/
        template >
        std::pair equal_range(const TComparable& _value)
        {
            return _equal_range(m_Container, m_Compare, _value);
        }

        template >
        std::pair equal_range(const TComparable& _value) const
        {
            return _equal_range(m_Container, m_Compare, _value);
        }

        template >
        iterator find_last(const TComparable& _value)
        {
            return _find_last(m_Container, m_Compare, _value);
        }

        template >
        const_iterator find_last(const TComparable& _value) const
        {
            return _find_last(m_Container, m_Compare, _value);
        }

        template >
        iterator erase_all_of(const TComparable& _value)
        {
            auto range = _equal_range(m_Container, m_Compare, _value);
            return m_Container.erase(range.first, range.second);
        }

        template >
        size_type count(const TComparable& _value) const
        {
            auto range = _equal_range(m_Container, m_Compare, _value);
            return std::distance(range.first, range.second);
        }

        template >
        void unique()
        {
            m_Container.erase(std::unique(std::begin(m_Container), std::end(m_Container),
                [&compare = m_Compare](const auto& _lhs, const auto& _rhs)
            {
                return !compare(_lhs, _rhs) && !compare(_rhs, _lhs);
            }
            ), end());
        }

        /*#####
        # comparison stuff
        #####*/
        bool operator ==(const BaseSkipList& _other) const
        {
            return std::equal(begin(), end(), std::begin(_other), std::end(_other),
                [&compare = m_Compare](const auto& _elLhs, const auto& _elRhs)
            {
                return !compare(_elLhs, _elRhs) && !compare(_elRhs, _elLhs);
            }
            );
        }

        friend bool operator !=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs == _rhs);
        }

        bool operator <(const BaseSkipList& _other) const
        {
            return std::lexicographical_compare(begin(), end(), std::begin(_other), std::end(_other), m_Compare);
        }

        friend bool operator >=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs < _rhs);
        }

        friend bool operator >(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return _rhs < _lhs;
        }

        friend bool operator <=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs > _rhs);
        }

        /*#####
        # Iterator stuff
        #####*/
        iterator begin() noexcept
        {
            return std::begin(m_Container);
        }

        const_iterator begin() const noexcept
        {
            return std::begin(m_Container);
        }

        const_iterator cbegin() const noexcept
        {
            return std::cbegin(m_Container);
        }

        iterator end() noexcept
        {
            return std::end(m_Container);
        }

        const_iterator end() const noexcept
        {
            return std::end(m_Container);
        }

        const_iterator cend() const noexcept
        {
            return std::cend(m_Container);
        }

        iterator rbegin() noexcept
        {
            return std::rbegin(m_Container);
        }

        const_reverse_iterator rbegin() const noexcept
        {
            return std::rbegin(m_Container);
        }

        const_reverse_iterator crbegin() const noexcept
        {
            return std::crbegin(m_Container);
        }

        iterator rend() noexcept
        {
            return std::rend(m_Container);
        }

        const_reverse_iterator rend() const noexcept
        {
            return std::rend(m_Container);
        }

        const_reverse_iterator crend() const noexcept
        {
            return std::crend(m_Container);
        }

    private:
        Container m_Container;
        Compare m_Compare;

        template 
        std::pair _insert(TValueType&& _value)
        {
            auto itr = _lower_bound(m_Container, m_Compare, _value);
            if constexpr (UniqueElements)
            {
                if (itr == end() || m_Compare(_value, *itr))
                {
                    m_Container.insert(itr, std::forward(_value));
                    return { itr, true };
                }
            }
            else
            {
                m_Container.insert(itr, std::forward(_value));
                return { itr, true };
            }
            return { itr, false };
        }

        template 
        static auto _find(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            auto itr = _lower_bound(_container, _compare, _value);
            if (itr != std::end(_container) && !_compare(_value, *itr))
                return itr;
            return std::end(_container);
        }

        template 
        static auto _find_last(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            auto range = _equal_range(_container, _compare, _value);
            auto dist = std::distance(range.first, range.second);
            if (0 < dist)
            {
                std::advance(range.first, dist - 1);
                return range.first;
            }
            return std::end(_container);
        }

        template 
        static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
        }

        template 
        static auto _upper_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::upper_bound(std::begin(_container), std::end(_container), _value, _compare);
        }

        template 
        static auto _equal_range(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::equal_range(std::begin(_container), std::end(_container), _value, _compare);
        }
    };
} // namespace detail

template , class Container = std::vector>
using SkipList = detail::BaseSkipList;
template , class Container = std::vector>
using MultiSkipList = detail::BaseSkipList;

下面是一个简单的示例程序;请注意,在第二个示例中,Test对象的成员D12被用作键。我们可以搜索具体的对象或简单的键值。

代码语言:javascript
复制
int main()
{
    SkipList ints({ 3, 4, 1, 2 });

    auto intItr = ints.find(1);
    // intItr = ints.find_last(5);  // not available for SkipLists
    ints.insert({ 5, 2, 3, 10, 0 });
    ints.erase(4);


    struct TestCompare
    {
        bool operator ()(const Test& _lhs, const Test& _rhs) const
        {
            return _lhs.x < _rhs.x;
        }

        bool operator ()(const Test& _lhs, int _rhs) const
        {
            return _lhs.x < _rhs;
        }

        bool operator ()(int _lhs, const Test& _rhs) const
        {
            return _lhs < _rhs.x;
        }
    };

    MultiSkipList tests({
        { 3 },
        { 4 },
        { 1 },
        { 2 },
        { 2 }
    });
    auto testItr = tests.find(2);
    testItr = tests.find_last(2);
    if (testItr == tests.find_last(Test{ 2 }))
        tests.unique();             // that line removes the second Test object with x == 2
    auto count = tests.count(2);    // there is only one element with x == 2 left

    tests.insert({ { 5}, { 2}, {3}, {10}, {0} });   // again 2 of x == 2

    tests <= tests;
    tests == tests;

    tests.erase_all_of(2);      // all twos are gone
}
EN

回答 1

Code Review用户

发布于 2018-10-08 17:23:55

在使用了几天之后,我发现了一些错误,并想将它们分享给您。我还修正了clang中的编译错误,因为MultiSkipList函数没有以正确的方式禁用。

我仍然希望收到一些关于风格,可用性和正确性的意见。

代码语言:javascript
复制
#pragma once

#include 
#include 
#include 

namespace detail
{
    template , class Container = std::vector>
    class BaseSkipList
    {
    public:
        using container_type = Container;
        using value_type = typename Container::value_type;
        using size_type = typename Container::size_type;
        using iterator = typename Container::iterator;
        using reverse_iterator = typename Container::reverse_iterator;
        using const_iterator = typename Container::const_iterator;
        using const_reverse_iterator = typename Container::const_reverse_iterator;

        BaseSkipList() = default;

        explicit BaseSkipList(Compare _compare) :
            m_Compare(std::move(_compare))
        {
        }

        explicit BaseSkipList(Container _container, Compare _compare = Compare()) :
            m_Container(std::move(_container)),
            m_Compare(std::move(_compare))
        {
            std::sort(std::begin(m_Container), std::end(m_Container), m_Compare);
        }

        ~BaseSkipList() = default;

        BaseSkipList(const BaseSkipList&) = default;
        BaseSkipList(BaseSkipList&&) noexcept = default;

        BaseSkipList& operator =(const BaseSkipList&) = default;
        BaseSkipList& operator =(BaseSkipList&&) noexcept = default;

        bool empty() const
        {
            return std::empty(m_Container);
        }

        size_type size() const
        {
            return std::size(m_Container);
        }

        void clear()
        {
            m_Container.clear();
        }

        void reserve(size_type _new_cap)
        {
            m_Container.reserve(_new_cap);
        }

        size_type capacity() const noexcept
        {
            return m_Container.capacity();
        }

        void shrink_to_fit()
        {
            return m_Container.shrink_to_fit();
        }

        template 
        std::pair insert(TValueType&& _value)
        {
            return _insert(std::forward(_value));
        }

        template 
        void insert(TIterator _itr, const TIterator& _end)
        {
            for (; _itr != _end; ++_itr)
            {
                _insert(*_itr);
            }
        }

        void insert(std::initializer_list _ilist)
        {
            insert(std::begin(_ilist), std::end(_ilist));
        }

        iterator erase(const_iterator _itr)
        {
            return m_Container.erase(_itr);
        }

        iterator erase(const_iterator _first, const_iterator _last)
        {
            return m_Container.erase(_first, _last);
        }

        template 
        iterator erase(const TComparable& _value)
        {
            auto itr = std::lower_bound(std::begin(m_Container), std::end(m_Container), _value, m_Compare);
            if (itr != end())
            {
                return m_Container.erase(itr);
            }
            return end();
        }

        template 
        iterator find(const TComparable& _value)
        {
            return _find(m_Container, m_Compare, _value);
        }

        template 
        const_iterator find(const TComparable& _value) const
        {
            return _find(m_Container, m_Compare, _value);
        }

        template 
        iterator lower_bound(const TComparable& _value)
        {
            return _lower_bound(m_Container, m_Compare, _value);
        }

        template 
        const_iterator lower_bound(const TComparable& _value) const
        {
            return _lower_bound(m_Container, m_Compare, _value);
        }

        template 
        iterator upper_bound(const TComparable& _value)
        {
            return _upper_bound(m_Container, m_Compare, _value);
        }

        template 
        const_iterator upper_bound(const TComparable& _value) const
        {
            return _upper_bound(m_Container, m_Compare, _value);
        }

        template 
        bool contains(const TComparable& _value) const
        {
            return find(_value) != end();
        }

        /*#####
        # multi element stuff
        #####*/
        template >
        std::pair equal_range(const TComparable& _value)
        {
            return _equal_range(m_Container, m_Compare, _value);
        }

        template >
        std::pair equal_range(const TComparable& _value) const
        {
            return _equal_range(m_Container, m_Compare, _value);
        }

        template >
        iterator find_last(const TComparable& _value)
        {
            return _find_last(m_Container, m_Compare, _value);
        }

        template >
        const_iterator find_last(const TComparable& _value) const
        {
            return _find_last(m_Container, m_Compare, _value);
        }

        template >
        iterator erase_all_of(const TComparable& _value)
        {
            auto range = _equal_range(m_Container, m_Compare, _value);
            return m_Container.erase(range.first, range.second);
        }

        template >
        size_type count(const TComparable& _value) const
        {
            auto range = _equal_range(m_Container, m_Compare, _value);
            return std::distance(range.first, range.second);
        }

        template >
        void unique()
        {
            m_Container.erase(std::unique(std::begin(m_Container), std::end(m_Container),
                                          [&compare = m_Compare](const auto& _lhs, const auto& _rhs)
                                          {
                                              return !compare(_lhs, _rhs) && !compare(_rhs, _lhs);
                                          }), end());
        }

        /*#####
        # comparison stuff
        #####*/
        bool operator ==(const BaseSkipList& _other) const
        {
            return std::equal(begin(), end(), std::begin(_other), std::end(_other),
                              [&compare = m_Compare](const auto& _elLhs, const auto& _elRhs)
                              {
                                  return !compare(_elLhs, _elRhs) && !compare(_elRhs, _elLhs);
                              });
        }

        friend bool operator !=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs == _rhs);
        }

        bool operator <(const BaseSkipList& _other) const
        {
            return std::lexicographical_compare(begin(), end(), std::begin(_other), std::end(_other), m_Compare);
        }

        friend bool operator >=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs < _rhs);
        }

        friend bool operator >(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return _rhs < _lhs;
        }

        friend bool operator <=(const BaseSkipList& _lhs, const BaseSkipList& _rhs)
        {
            return !(_lhs > _rhs);
        }

        /*#####
        # Iterator stuff
        #####*/
        iterator begin() noexcept
        {
            return std::begin(m_Container);
        }

        const_iterator begin() const noexcept
        {
            return std::begin(m_Container);
        }

        const_iterator cbegin() const noexcept
        {
            return std::cbegin(m_Container);
        }

        iterator end() noexcept
        {
            return std::end(m_Container);
        }

        const_iterator end() const noexcept
        {
            return std::end(m_Container);
        }

        const_iterator cend() const noexcept
        {
            return std::cend(m_Container);
        }

        iterator rbegin() noexcept
        {
            return std::rbegin(m_Container);
        }

        const_reverse_iterator rbegin() const noexcept
        {
            return std::rbegin(m_Container);
        }

        const_reverse_iterator crbegin() const noexcept
        {
            return std::crbegin(m_Container);
        }

        iterator rend() noexcept
        {
            return std::rend(m_Container);
        }

        const_reverse_iterator rend() const noexcept
        {
            return std::rend(m_Container);
        }

        const_reverse_iterator crend() const noexcept
        {
            return std::crend(m_Container);
        }

    private:
        Container m_Container;

        Compare m_Compare;

        template 
        std::pair _insert(TValueType&& _value)
        {
            auto itr = _lower_bound(m_Container, m_Compare, _value);
            if constexpr (UniqueElements)
            {
                if (itr == end() || m_Compare(_value, *itr))
                {
                    m_Container.insert(itr, std::forward(_value));
                    return {itr, true};
                }
            }
            else
            {
                m_Container.insert(itr, std::forward(_value));
                return {itr, true};
            }
            return {itr, false};
        }

        template 
        static auto _find(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            auto itr = _lower_bound(_container, _compare, _value);
            if (itr != std::end(_container) && !_compare(_value, *itr))
            {
                return itr;
            }
            return std::end(_container);
        }

        template 
        static auto _find_last(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            auto [begin, end] = _equal_range(_container, _compare, _value);
            auto dist         = std::distance(begin, end);
            if (0 < dist)
            {
                std::advance(begin, dist - 1);
                return begin;
            }
            return std::end(_container);
        }

        template 
        static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
        }

        template 
        static auto _upper_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::upper_bound(std::begin(_container), std::end(_container), _value, _compare);
        }

        template 
        static auto _equal_range(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
        {
            return std::equal_range(std::begin(_container), std::end(_container), _value, _compare);
        }
    };
} // namespace detail

template , class Container = std::vector>
using SkipList = detail::BaseSkipList;
template , class Container = std::vector>
using MultiSkipList = detail::BaseSkipList;
票数 -1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/205029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档