Generic (Multi)SkipList adapter class











up vote
1
down vote

favorite












Today I have written my first implementation of a SkipList (on top of a std::vector), because I was tired in using std::lower_bound and the other functions manually. I tried to create it as generic as possible to provide the user as much freedom as healthy.
During the implementation I always had an eye on the std::set and std::multiset documentations, to keep the interface similar to those.



At first I decided to hide the non-const iterators from the public interface to prevent the SkipList from getting manipulated on accident, but after the first iteration the usage it was very inelegant. At latest when you tried to use just one property of the value_type Objects as key it revealed its weaknesses.To change any of the other variables of that element I had to remove it first and insert it again afterwards. That was neither good, clean or elegant; that's the reason why I decided to expose the non-const-iterators, too. Btw, std::set and std::multiset expose them, too, thus it shouldn't be a huge surprise for the user of that classes.



Here is the code for both, the SkipList (unique elements) and the MultiSkipList.



#include <vector>
#include <algorithm>
#include <iterator>

namespace detail
{
template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
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 <class TValueType>
std::pair<iterator, bool> insert(TValueType&& _value)
{
return _insert(begin(), std::forward<TValueType>(_value));
}

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

void insert(std::initializer_list<value_type> _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 <class TComparable>
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 <class TComparable>
iterator find(const TComparable& _value)
{
return _find(m_Container, m_Compare, _value);
}

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

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

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

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

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

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

/*#####
# multi element stuff
#####*/
template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
std::pair<iterator, iterator> equal_range(const TComparable& _value)
{
return _equal_range(m_Container, m_Compare, _value);
}

template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
{
return _equal_range(m_Container, m_Compare, _value);
}

template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
iterator find_last(const TComparable& _value)
{
return _find_last(m_Container, m_Compare, _value);
}

template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
const_iterator find_last(const TComparable& _value) const
{
return _find_last(m_Container, m_Compare, _value);
}

template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
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 <class TComparable, typename = std::enable_if_t<!UniqueElements>>
size_type count(const TComparable& _value) const
{
auto range = _equal_range(m_Container, m_Compare, _value);
return std::distance(range.first, range.second);
}

template <class TValueType = value_type, typename = std::enable_if_t<!UniqueElements>>
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 <class TValueType>
std::pair<iterator, bool> _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<TValueType>(_value));
return { itr, true };
}
}
else
{
m_Container.insert(itr, std::forward<TValueType>(_value));
return { itr, true };
}
return { itr, false };
}

template <class TContainer, class TCompare, class TComparable>
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 <class TContainer, class TCompare, class TComparable>
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 <class TContainer, class TCompare, class TComparable>
static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
{
return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
}

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

template <class TContainer, class TCompare, class TComparable>
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 T, class Compare = std::less<>, class Container = std::vector<T>>
using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
template <class T, class Compare = std::less<>, class Container = std::vector<T>>
using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;


Here is a simple example program; please be aware, that in the second example the member x of the Test objects is used as key. We can search for concrete objects or simply key values.



int main()
{
SkipList<int> 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<Test, TestCompare> 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
}









share|improve this question
















bumped to the homepage by Community 14 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.



















    up vote
    1
    down vote

    favorite












    Today I have written my first implementation of a SkipList (on top of a std::vector), because I was tired in using std::lower_bound and the other functions manually. I tried to create it as generic as possible to provide the user as much freedom as healthy.
    During the implementation I always had an eye on the std::set and std::multiset documentations, to keep the interface similar to those.



    At first I decided to hide the non-const iterators from the public interface to prevent the SkipList from getting manipulated on accident, but after the first iteration the usage it was very inelegant. At latest when you tried to use just one property of the value_type Objects as key it revealed its weaknesses.To change any of the other variables of that element I had to remove it first and insert it again afterwards. That was neither good, clean or elegant; that's the reason why I decided to expose the non-const-iterators, too. Btw, std::set and std::multiset expose them, too, thus it shouldn't be a huge surprise for the user of that classes.



    Here is the code for both, the SkipList (unique elements) and the MultiSkipList.



    #include <vector>
    #include <algorithm>
    #include <iterator>

    namespace detail
    {
    template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
    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 <class TValueType>
    std::pair<iterator, bool> insert(TValueType&& _value)
    {
    return _insert(begin(), std::forward<TValueType>(_value));
    }

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

    void insert(std::initializer_list<value_type> _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 <class TComparable>
    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 <class TComparable>
    iterator find(const TComparable& _value)
    {
    return _find(m_Container, m_Compare, _value);
    }

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

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

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

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

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

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

    /*#####
    # multi element stuff
    #####*/
    template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    std::pair<iterator, iterator> equal_range(const TComparable& _value)
    {
    return _equal_range(m_Container, m_Compare, _value);
    }

    template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
    {
    return _equal_range(m_Container, m_Compare, _value);
    }

    template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    iterator find_last(const TComparable& _value)
    {
    return _find_last(m_Container, m_Compare, _value);
    }

    template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    const_iterator find_last(const TComparable& _value) const
    {
    return _find_last(m_Container, m_Compare, _value);
    }

    template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    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 <class TComparable, typename = std::enable_if_t<!UniqueElements>>
    size_type count(const TComparable& _value) const
    {
    auto range = _equal_range(m_Container, m_Compare, _value);
    return std::distance(range.first, range.second);
    }

    template <class TValueType = value_type, typename = std::enable_if_t<!UniqueElements>>
    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 <class TValueType>
    std::pair<iterator, bool> _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<TValueType>(_value));
    return { itr, true };
    }
    }
    else
    {
    m_Container.insert(itr, std::forward<TValueType>(_value));
    return { itr, true };
    }
    return { itr, false };
    }

    template <class TContainer, class TCompare, class TComparable>
    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 <class TContainer, class TCompare, class TComparable>
    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 <class TContainer, class TCompare, class TComparable>
    static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
    {
    return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
    }

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

    template <class TContainer, class TCompare, class TComparable>
    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 T, class Compare = std::less<>, class Container = std::vector<T>>
    using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
    template <class T, class Compare = std::less<>, class Container = std::vector<T>>
    using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;


    Here is a simple example program; please be aware, that in the second example the member x of the Test objects is used as key. We can search for concrete objects or simply key values.



    int main()
    {
    SkipList<int> 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<Test, TestCompare> 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
    }









    share|improve this question
















    bumped to the homepage by Community 14 hours ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.

















      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      Today I have written my first implementation of a SkipList (on top of a std::vector), because I was tired in using std::lower_bound and the other functions manually. I tried to create it as generic as possible to provide the user as much freedom as healthy.
      During the implementation I always had an eye on the std::set and std::multiset documentations, to keep the interface similar to those.



      At first I decided to hide the non-const iterators from the public interface to prevent the SkipList from getting manipulated on accident, but after the first iteration the usage it was very inelegant. At latest when you tried to use just one property of the value_type Objects as key it revealed its weaknesses.To change any of the other variables of that element I had to remove it first and insert it again afterwards. That was neither good, clean or elegant; that's the reason why I decided to expose the non-const-iterators, too. Btw, std::set and std::multiset expose them, too, thus it shouldn't be a huge surprise for the user of that classes.



      Here is the code for both, the SkipList (unique elements) and the MultiSkipList.



      #include <vector>
      #include <algorithm>
      #include <iterator>

      namespace detail
      {
      template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
      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 <class TValueType>
      std::pair<iterator, bool> insert(TValueType&& _value)
      {
      return _insert(begin(), std::forward<TValueType>(_value));
      }

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

      void insert(std::initializer_list<value_type> _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 <class TComparable>
      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 <class TComparable>
      iterator find(const TComparable& _value)
      {
      return _find(m_Container, m_Compare, _value);
      }

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

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

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

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

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

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

      /*#####
      # multi element stuff
      #####*/
      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      std::pair<iterator, iterator> equal_range(const TComparable& _value)
      {
      return _equal_range(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
      {
      return _equal_range(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      iterator find_last(const TComparable& _value)
      {
      return _find_last(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      const_iterator find_last(const TComparable& _value) const
      {
      return _find_last(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      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 <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      size_type count(const TComparable& _value) const
      {
      auto range = _equal_range(m_Container, m_Compare, _value);
      return std::distance(range.first, range.second);
      }

      template <class TValueType = value_type, typename = std::enable_if_t<!UniqueElements>>
      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 <class TValueType>
      std::pair<iterator, bool> _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<TValueType>(_value));
      return { itr, true };
      }
      }
      else
      {
      m_Container.insert(itr, std::forward<TValueType>(_value));
      return { itr, true };
      }
      return { itr, false };
      }

      template <class TContainer, class TCompare, class TComparable>
      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 <class TContainer, class TCompare, class TComparable>
      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 <class TContainer, class TCompare, class TComparable>
      static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
      {
      return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
      }

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

      template <class TContainer, class TCompare, class TComparable>
      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 T, class Compare = std::less<>, class Container = std::vector<T>>
      using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
      template <class T, class Compare = std::less<>, class Container = std::vector<T>>
      using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;


      Here is a simple example program; please be aware, that in the second example the member x of the Test objects is used as key. We can search for concrete objects or simply key values.



      int main()
      {
      SkipList<int> 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<Test, TestCompare> 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
      }









      share|improve this question















      Today I have written my first implementation of a SkipList (on top of a std::vector), because I was tired in using std::lower_bound and the other functions manually. I tried to create it as generic as possible to provide the user as much freedom as healthy.
      During the implementation I always had an eye on the std::set and std::multiset documentations, to keep the interface similar to those.



      At first I decided to hide the non-const iterators from the public interface to prevent the SkipList from getting manipulated on accident, but after the first iteration the usage it was very inelegant. At latest when you tried to use just one property of the value_type Objects as key it revealed its weaknesses.To change any of the other variables of that element I had to remove it first and insert it again afterwards. That was neither good, clean or elegant; that's the reason why I decided to expose the non-const-iterators, too. Btw, std::set and std::multiset expose them, too, thus it shouldn't be a huge surprise for the user of that classes.



      Here is the code for both, the SkipList (unique elements) and the MultiSkipList.



      #include <vector>
      #include <algorithm>
      #include <iterator>

      namespace detail
      {
      template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
      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 <class TValueType>
      std::pair<iterator, bool> insert(TValueType&& _value)
      {
      return _insert(begin(), std::forward<TValueType>(_value));
      }

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

      void insert(std::initializer_list<value_type> _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 <class TComparable>
      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 <class TComparable>
      iterator find(const TComparable& _value)
      {
      return _find(m_Container, m_Compare, _value);
      }

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

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

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

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

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

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

      /*#####
      # multi element stuff
      #####*/
      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      std::pair<iterator, iterator> equal_range(const TComparable& _value)
      {
      return _equal_range(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
      {
      return _equal_range(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      iterator find_last(const TComparable& _value)
      {
      return _find_last(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      const_iterator find_last(const TComparable& _value) const
      {
      return _find_last(m_Container, m_Compare, _value);
      }

      template <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      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 <class TComparable, typename = std::enable_if_t<!UniqueElements>>
      size_type count(const TComparable& _value) const
      {
      auto range = _equal_range(m_Container, m_Compare, _value);
      return std::distance(range.first, range.second);
      }

      template <class TValueType = value_type, typename = std::enable_if_t<!UniqueElements>>
      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 <class TValueType>
      std::pair<iterator, bool> _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<TValueType>(_value));
      return { itr, true };
      }
      }
      else
      {
      m_Container.insert(itr, std::forward<TValueType>(_value));
      return { itr, true };
      }
      return { itr, false };
      }

      template <class TContainer, class TCompare, class TComparable>
      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 <class TContainer, class TCompare, class TComparable>
      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 <class TContainer, class TCompare, class TComparable>
      static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
      {
      return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
      }

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

      template <class TContainer, class TCompare, class TComparable>
      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 T, class Compare = std::less<>, class Container = std::vector<T>>
      using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
      template <class T, class Compare = std::less<>, class Container = std::vector<T>>
      using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;


      Here is a simple example program; please be aware, that in the second example the member x of the Test objects is used as key. We can search for concrete objects or simply key values.



      int main()
      {
      SkipList<int> 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<Test, TestCompare> 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
      }






      c++ template-meta-programming c++17 skip-list






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Oct 5 at 23:05

























      asked Oct 5 at 18:32









      DNKpp

      58629




      58629





      bumped to the homepage by Community 14 hours ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 14 hours ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          0
          down vote













          After a few days in use, I discovered a few mistakes and would like to share them to you. I also fixed a compile error in clang, because the MultiSkipList functions weren't disabled in a correct way.



          I still would like to receive some opinions about style, usability and correctness.



          #pragma once

          #include <vector>
          #include <algorithm>
          #include <iterator>

          namespace detail
          {
          template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
          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 <class TValueType>
          std::pair<iterator, bool> insert(TValueType&& _value)
          {
          return _insert(std::forward<TValueType>(_value));
          }

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

          void insert(std::initializer_list<value_type> _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 <class TComparable>
          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 <class TComparable>
          iterator find(const TComparable& _value)
          {
          return _find(m_Container, m_Compare, _value);
          }

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

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

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

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

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

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

          /*#####
          # multi element stuff
          #####*/
          template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          std::pair<iterator, iterator> equal_range(const TComparable& _value)
          {
          return _equal_range(m_Container, m_Compare, _value);
          }

          template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
          {
          return _equal_range(m_Container, m_Compare, _value);
          }

          template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          iterator find_last(const TComparable& _value)
          {
          return _find_last(m_Container, m_Compare, _value);
          }

          template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          const_iterator find_last(const TComparable& _value) const
          {
          return _find_last(m_Container, m_Compare, _value);
          }

          template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          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 <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          size_type count(const TComparable& _value) const
          {
          auto range = _equal_range(m_Container, m_Compare, _value);
          return std::distance(range.first, range.second);
          }

          template <bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
          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 <class TValueType>
          std::pair<iterator, bool> _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<TValueType>(_value));
          return {itr, true};
          }
          }
          else
          {
          m_Container.insert(itr, std::forward<TValueType>(_value));
          return {itr, true};
          }
          return {itr, false};
          }

          template <class TContainer, class TCompare, class TComparable>
          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 <class TContainer, class TCompare, class TComparable>
          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 <class TContainer, class TCompare, class TComparable>
          static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
          {
          return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
          }

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

          template <class TContainer, class TCompare, class TComparable>
          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 T, class Compare = std::less<>, class Container = std::vector<T>>
          using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
          template <class T, class Compare = std::less<>, class Container = std::vector<T>>
          using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;





          share|improve this answer




























            up vote
            0
            down vote













            Is that a skip list?



            Frankly, I don't see how it can be described as a skip list. Skip lists are meant to replace balanced trees, since they offer a similar service (fast insertion in a sorted container -so fast look-up also) with the added value of greater simplicity; they work by having different tracks -some faster, some more complete- to go through the elements they contain.



            Then what is it exactly?



            I believe that what you did was to build a kind of generic interface over containers, and then use that interface to provide what you thought was easier access to standard algorithms. But please correct me if I'm wrong. If not -if I'm right- what you did amounts to a crime in the eye of the STL police. Decoupling containers and algorithms is one of the greatest features of the STL, and going back on that -even if it involves some clever genericity- would buy you a direct ticket to the Java prison.



            What you could have done instead



            I understand that typing algorithm(std::begin(container), std::end(container); seems backwards when so many other languages have algorithm(container) or even container.algorithm. And I would very much agree that saving keystrokes is worth investing in the long term. Then simply take all those static private functions, and make them free and public, and put them into their own namespace.



            namespace adapt {
            template <typename Container, typename Compare = std::less<>>
            void sort(Container& container, Compare cmp) {
            std::sort(std::begin(container), std::end(container), cmp);
            }
            // ... and so on
            }


            Then if those toilsome keystrokes really bother you, it's only a matter of introducing the adapted function at the beginning of your file:



            using adapt::lower_bound;
            // ...
            lower_bound(my_vec, my_val);


            Or, if you want to be very clever, and aren't afraid of evil hacks and macros, you can write a generic adaptor for algorithms. It could be a lot cleaner if there were a standard way to turn generic functions into generic lambdas (because the latter can be deduced whereas the former can't), but it isn't that ugly either:



            #include <iostream>
            #include <algorithm>
            #include <vector>

            #define AS_LAMBDA(fn) (auto&&... args) { return fn(std::forward<decltype(args)>(args)...); }

            #define CONTAINER_APPLY(algorithm, container, ...)
            container_apply_impl(AS_LAMBDA(algorithm), container, __VA_ARGS__)

            template <typename Algorithm, typename Container, typename... Args>
            auto container_apply_impl(Algorithm algorithm, Container& container, Args&&... args) {
            return algorithm(std::begin(container), std::end(container), std::forward<Args>(args)...);
            }

            int main() {
            std::vector<int> vec{1, 2, 3, 4, 8, 7, 2, 8, 6, 4, 2, 3 };
            CONTAINER_APPLY(std::for_each, vec, (auto i) { std::cout << i << ", "; });
            }





            share|improve this answer





















              Your Answer





              StackExchange.ifUsing("editor", function () {
              return StackExchange.using("mathjaxEditing", function () {
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              });
              });
              }, "mathjax-editing");

              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "196"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f205029%2fgeneric-multiskiplist-adapter-class%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              0
              down vote













              After a few days in use, I discovered a few mistakes and would like to share them to you. I also fixed a compile error in clang, because the MultiSkipList functions weren't disabled in a correct way.



              I still would like to receive some opinions about style, usability and correctness.



              #pragma once

              #include <vector>
              #include <algorithm>
              #include <iterator>

              namespace detail
              {
              template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
              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 <class TValueType>
              std::pair<iterator, bool> insert(TValueType&& _value)
              {
              return _insert(std::forward<TValueType>(_value));
              }

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

              void insert(std::initializer_list<value_type> _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 <class TComparable>
              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 <class TComparable>
              iterator find(const TComparable& _value)
              {
              return _find(m_Container, m_Compare, _value);
              }

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

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

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

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

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

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

              /*#####
              # multi element stuff
              #####*/
              template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              std::pair<iterator, iterator> equal_range(const TComparable& _value)
              {
              return _equal_range(m_Container, m_Compare, _value);
              }

              template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
              {
              return _equal_range(m_Container, m_Compare, _value);
              }

              template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              iterator find_last(const TComparable& _value)
              {
              return _find_last(m_Container, m_Compare, _value);
              }

              template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              const_iterator find_last(const TComparable& _value) const
              {
              return _find_last(m_Container, m_Compare, _value);
              }

              template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              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 <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              size_type count(const TComparable& _value) const
              {
              auto range = _equal_range(m_Container, m_Compare, _value);
              return std::distance(range.first, range.second);
              }

              template <bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
              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 <class TValueType>
              std::pair<iterator, bool> _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<TValueType>(_value));
              return {itr, true};
              }
              }
              else
              {
              m_Container.insert(itr, std::forward<TValueType>(_value));
              return {itr, true};
              }
              return {itr, false};
              }

              template <class TContainer, class TCompare, class TComparable>
              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 <class TContainer, class TCompare, class TComparable>
              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 <class TContainer, class TCompare, class TComparable>
              static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
              {
              return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
              }

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

              template <class TContainer, class TCompare, class TComparable>
              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 T, class Compare = std::less<>, class Container = std::vector<T>>
              using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
              template <class T, class Compare = std::less<>, class Container = std::vector<T>>
              using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;





              share|improve this answer

























                up vote
                0
                down vote













                After a few days in use, I discovered a few mistakes and would like to share them to you. I also fixed a compile error in clang, because the MultiSkipList functions weren't disabled in a correct way.



                I still would like to receive some opinions about style, usability and correctness.



                #pragma once

                #include <vector>
                #include <algorithm>
                #include <iterator>

                namespace detail
                {
                template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
                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 <class TValueType>
                std::pair<iterator, bool> insert(TValueType&& _value)
                {
                return _insert(std::forward<TValueType>(_value));
                }

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

                void insert(std::initializer_list<value_type> _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 <class TComparable>
                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 <class TComparable>
                iterator find(const TComparable& _value)
                {
                return _find(m_Container, m_Compare, _value);
                }

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

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

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

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

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

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

                /*#####
                # multi element stuff
                #####*/
                template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                std::pair<iterator, iterator> equal_range(const TComparable& _value)
                {
                return _equal_range(m_Container, m_Compare, _value);
                }

                template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
                {
                return _equal_range(m_Container, m_Compare, _value);
                }

                template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                iterator find_last(const TComparable& _value)
                {
                return _find_last(m_Container, m_Compare, _value);
                }

                template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                const_iterator find_last(const TComparable& _value) const
                {
                return _find_last(m_Container, m_Compare, _value);
                }

                template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                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 <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                size_type count(const TComparable& _value) const
                {
                auto range = _equal_range(m_Container, m_Compare, _value);
                return std::distance(range.first, range.second);
                }

                template <bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                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 <class TValueType>
                std::pair<iterator, bool> _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<TValueType>(_value));
                return {itr, true};
                }
                }
                else
                {
                m_Container.insert(itr, std::forward<TValueType>(_value));
                return {itr, true};
                }
                return {itr, false};
                }

                template <class TContainer, class TCompare, class TComparable>
                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 <class TContainer, class TCompare, class TComparable>
                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 <class TContainer, class TCompare, class TComparable>
                static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
                {
                return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
                }

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

                template <class TContainer, class TCompare, class TComparable>
                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 T, class Compare = std::less<>, class Container = std::vector<T>>
                using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
                template <class T, class Compare = std::less<>, class Container = std::vector<T>>
                using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;





                share|improve this answer























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  After a few days in use, I discovered a few mistakes and would like to share them to you. I also fixed a compile error in clang, because the MultiSkipList functions weren't disabled in a correct way.



                  I still would like to receive some opinions about style, usability and correctness.



                  #pragma once

                  #include <vector>
                  #include <algorithm>
                  #include <iterator>

                  namespace detail
                  {
                  template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
                  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 <class TValueType>
                  std::pair<iterator, bool> insert(TValueType&& _value)
                  {
                  return _insert(std::forward<TValueType>(_value));
                  }

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

                  void insert(std::initializer_list<value_type> _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 <class TComparable>
                  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 <class TComparable>
                  iterator find(const TComparable& _value)
                  {
                  return _find(m_Container, m_Compare, _value);
                  }

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

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

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

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

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

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

                  /*#####
                  # multi element stuff
                  #####*/
                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  std::pair<iterator, iterator> equal_range(const TComparable& _value)
                  {
                  return _equal_range(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
                  {
                  return _equal_range(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  iterator find_last(const TComparable& _value)
                  {
                  return _find_last(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  const_iterator find_last(const TComparable& _value) const
                  {
                  return _find_last(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  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 <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  size_type count(const TComparable& _value) const
                  {
                  auto range = _equal_range(m_Container, m_Compare, _value);
                  return std::distance(range.first, range.second);
                  }

                  template <bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  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 <class TValueType>
                  std::pair<iterator, bool> _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<TValueType>(_value));
                  return {itr, true};
                  }
                  }
                  else
                  {
                  m_Container.insert(itr, std::forward<TValueType>(_value));
                  return {itr, true};
                  }
                  return {itr, false};
                  }

                  template <class TContainer, class TCompare, class TComparable>
                  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 <class TContainer, class TCompare, class TComparable>
                  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 <class TContainer, class TCompare, class TComparable>
                  static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
                  {
                  return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
                  }

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

                  template <class TContainer, class TCompare, class TComparable>
                  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 T, class Compare = std::less<>, class Container = std::vector<T>>
                  using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
                  template <class T, class Compare = std::less<>, class Container = std::vector<T>>
                  using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;





                  share|improve this answer












                  After a few days in use, I discovered a few mistakes and would like to share them to you. I also fixed a compile error in clang, because the MultiSkipList functions weren't disabled in a correct way.



                  I still would like to receive some opinions about style, usability and correctness.



                  #pragma once

                  #include <vector>
                  #include <algorithm>
                  #include <iterator>

                  namespace detail
                  {
                  template <class T, bool UniqueElements, class Compare = std::less<>, class Container = std::vector<T>>
                  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 <class TValueType>
                  std::pair<iterator, bool> insert(TValueType&& _value)
                  {
                  return _insert(std::forward<TValueType>(_value));
                  }

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

                  void insert(std::initializer_list<value_type> _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 <class TComparable>
                  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 <class TComparable>
                  iterator find(const TComparable& _value)
                  {
                  return _find(m_Container, m_Compare, _value);
                  }

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

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

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

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

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

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

                  /*#####
                  # multi element stuff
                  #####*/
                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  std::pair<iterator, iterator> equal_range(const TComparable& _value)
                  {
                  return _equal_range(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  std::pair<const_iterator, const_iterator> equal_range(const TComparable& _value) const
                  {
                  return _equal_range(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  iterator find_last(const TComparable& _value)
                  {
                  return _find_last(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  const_iterator find_last(const TComparable& _value) const
                  {
                  return _find_last(m_Container, m_Compare, _value);
                  }

                  template <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  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 <class TComparable, bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  size_type count(const TComparable& _value) const
                  {
                  auto range = _equal_range(m_Container, m_Compare, _value);
                  return std::distance(range.first, range.second);
                  }

                  template <bool Unique = UniqueElements, typename = std::enable_if_t<!Unique>>
                  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 <class TValueType>
                  std::pair<iterator, bool> _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<TValueType>(_value));
                  return {itr, true};
                  }
                  }
                  else
                  {
                  m_Container.insert(itr, std::forward<TValueType>(_value));
                  return {itr, true};
                  }
                  return {itr, false};
                  }

                  template <class TContainer, class TCompare, class TComparable>
                  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 <class TContainer, class TCompare, class TComparable>
                  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 <class TContainer, class TCompare, class TComparable>
                  static auto _lower_bound(TContainer&& _container, TCompare&& _compare, const TComparable& _value)
                  {
                  return std::lower_bound(std::begin(_container), std::end(_container), _value, _compare);
                  }

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

                  template <class TContainer, class TCompare, class TComparable>
                  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 T, class Compare = std::less<>, class Container = std::vector<T>>
                  using SkipList = detail::BaseSkipList<T, true, Compare, Container>;
                  template <class T, class Compare = std::less<>, class Container = std::vector<T>>
                  using MultiSkipList = detail::BaseSkipList<T, false, Compare, Container>;






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Oct 8 at 17:23









                  DNKpp

                  58629




                  58629
























                      up vote
                      0
                      down vote













                      Is that a skip list?



                      Frankly, I don't see how it can be described as a skip list. Skip lists are meant to replace balanced trees, since they offer a similar service (fast insertion in a sorted container -so fast look-up also) with the added value of greater simplicity; they work by having different tracks -some faster, some more complete- to go through the elements they contain.



                      Then what is it exactly?



                      I believe that what you did was to build a kind of generic interface over containers, and then use that interface to provide what you thought was easier access to standard algorithms. But please correct me if I'm wrong. If not -if I'm right- what you did amounts to a crime in the eye of the STL police. Decoupling containers and algorithms is one of the greatest features of the STL, and going back on that -even if it involves some clever genericity- would buy you a direct ticket to the Java prison.



                      What you could have done instead



                      I understand that typing algorithm(std::begin(container), std::end(container); seems backwards when so many other languages have algorithm(container) or even container.algorithm. And I would very much agree that saving keystrokes is worth investing in the long term. Then simply take all those static private functions, and make them free and public, and put them into their own namespace.



                      namespace adapt {
                      template <typename Container, typename Compare = std::less<>>
                      void sort(Container& container, Compare cmp) {
                      std::sort(std::begin(container), std::end(container), cmp);
                      }
                      // ... and so on
                      }


                      Then if those toilsome keystrokes really bother you, it's only a matter of introducing the adapted function at the beginning of your file:



                      using adapt::lower_bound;
                      // ...
                      lower_bound(my_vec, my_val);


                      Or, if you want to be very clever, and aren't afraid of evil hacks and macros, you can write a generic adaptor for algorithms. It could be a lot cleaner if there were a standard way to turn generic functions into generic lambdas (because the latter can be deduced whereas the former can't), but it isn't that ugly either:



                      #include <iostream>
                      #include <algorithm>
                      #include <vector>

                      #define AS_LAMBDA(fn) (auto&&... args) { return fn(std::forward<decltype(args)>(args)...); }

                      #define CONTAINER_APPLY(algorithm, container, ...)
                      container_apply_impl(AS_LAMBDA(algorithm), container, __VA_ARGS__)

                      template <typename Algorithm, typename Container, typename... Args>
                      auto container_apply_impl(Algorithm algorithm, Container& container, Args&&... args) {
                      return algorithm(std::begin(container), std::end(container), std::forward<Args>(args)...);
                      }

                      int main() {
                      std::vector<int> vec{1, 2, 3, 4, 8, 7, 2, 8, 6, 4, 2, 3 };
                      CONTAINER_APPLY(std::for_each, vec, (auto i) { std::cout << i << ", "; });
                      }





                      share|improve this answer

























                        up vote
                        0
                        down vote













                        Is that a skip list?



                        Frankly, I don't see how it can be described as a skip list. Skip lists are meant to replace balanced trees, since they offer a similar service (fast insertion in a sorted container -so fast look-up also) with the added value of greater simplicity; they work by having different tracks -some faster, some more complete- to go through the elements they contain.



                        Then what is it exactly?



                        I believe that what you did was to build a kind of generic interface over containers, and then use that interface to provide what you thought was easier access to standard algorithms. But please correct me if I'm wrong. If not -if I'm right- what you did amounts to a crime in the eye of the STL police. Decoupling containers and algorithms is one of the greatest features of the STL, and going back on that -even if it involves some clever genericity- would buy you a direct ticket to the Java prison.



                        What you could have done instead



                        I understand that typing algorithm(std::begin(container), std::end(container); seems backwards when so many other languages have algorithm(container) or even container.algorithm. And I would very much agree that saving keystrokes is worth investing in the long term. Then simply take all those static private functions, and make them free and public, and put them into their own namespace.



                        namespace adapt {
                        template <typename Container, typename Compare = std::less<>>
                        void sort(Container& container, Compare cmp) {
                        std::sort(std::begin(container), std::end(container), cmp);
                        }
                        // ... and so on
                        }


                        Then if those toilsome keystrokes really bother you, it's only a matter of introducing the adapted function at the beginning of your file:



                        using adapt::lower_bound;
                        // ...
                        lower_bound(my_vec, my_val);


                        Or, if you want to be very clever, and aren't afraid of evil hacks and macros, you can write a generic adaptor for algorithms. It could be a lot cleaner if there were a standard way to turn generic functions into generic lambdas (because the latter can be deduced whereas the former can't), but it isn't that ugly either:



                        #include <iostream>
                        #include <algorithm>
                        #include <vector>

                        #define AS_LAMBDA(fn) (auto&&... args) { return fn(std::forward<decltype(args)>(args)...); }

                        #define CONTAINER_APPLY(algorithm, container, ...)
                        container_apply_impl(AS_LAMBDA(algorithm), container, __VA_ARGS__)

                        template <typename Algorithm, typename Container, typename... Args>
                        auto container_apply_impl(Algorithm algorithm, Container& container, Args&&... args) {
                        return algorithm(std::begin(container), std::end(container), std::forward<Args>(args)...);
                        }

                        int main() {
                        std::vector<int> vec{1, 2, 3, 4, 8, 7, 2, 8, 6, 4, 2, 3 };
                        CONTAINER_APPLY(std::for_each, vec, (auto i) { std::cout << i << ", "; });
                        }





                        share|improve this answer























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote









                          Is that a skip list?



                          Frankly, I don't see how it can be described as a skip list. Skip lists are meant to replace balanced trees, since they offer a similar service (fast insertion in a sorted container -so fast look-up also) with the added value of greater simplicity; they work by having different tracks -some faster, some more complete- to go through the elements they contain.



                          Then what is it exactly?



                          I believe that what you did was to build a kind of generic interface over containers, and then use that interface to provide what you thought was easier access to standard algorithms. But please correct me if I'm wrong. If not -if I'm right- what you did amounts to a crime in the eye of the STL police. Decoupling containers and algorithms is one of the greatest features of the STL, and going back on that -even if it involves some clever genericity- would buy you a direct ticket to the Java prison.



                          What you could have done instead



                          I understand that typing algorithm(std::begin(container), std::end(container); seems backwards when so many other languages have algorithm(container) or even container.algorithm. And I would very much agree that saving keystrokes is worth investing in the long term. Then simply take all those static private functions, and make them free and public, and put them into their own namespace.



                          namespace adapt {
                          template <typename Container, typename Compare = std::less<>>
                          void sort(Container& container, Compare cmp) {
                          std::sort(std::begin(container), std::end(container), cmp);
                          }
                          // ... and so on
                          }


                          Then if those toilsome keystrokes really bother you, it's only a matter of introducing the adapted function at the beginning of your file:



                          using adapt::lower_bound;
                          // ...
                          lower_bound(my_vec, my_val);


                          Or, if you want to be very clever, and aren't afraid of evil hacks and macros, you can write a generic adaptor for algorithms. It could be a lot cleaner if there were a standard way to turn generic functions into generic lambdas (because the latter can be deduced whereas the former can't), but it isn't that ugly either:



                          #include <iostream>
                          #include <algorithm>
                          #include <vector>

                          #define AS_LAMBDA(fn) (auto&&... args) { return fn(std::forward<decltype(args)>(args)...); }

                          #define CONTAINER_APPLY(algorithm, container, ...)
                          container_apply_impl(AS_LAMBDA(algorithm), container, __VA_ARGS__)

                          template <typename Algorithm, typename Container, typename... Args>
                          auto container_apply_impl(Algorithm algorithm, Container& container, Args&&... args) {
                          return algorithm(std::begin(container), std::end(container), std::forward<Args>(args)...);
                          }

                          int main() {
                          std::vector<int> vec{1, 2, 3, 4, 8, 7, 2, 8, 6, 4, 2, 3 };
                          CONTAINER_APPLY(std::for_each, vec, (auto i) { std::cout << i << ", "; });
                          }





                          share|improve this answer












                          Is that a skip list?



                          Frankly, I don't see how it can be described as a skip list. Skip lists are meant to replace balanced trees, since they offer a similar service (fast insertion in a sorted container -so fast look-up also) with the added value of greater simplicity; they work by having different tracks -some faster, some more complete- to go through the elements they contain.



                          Then what is it exactly?



                          I believe that what you did was to build a kind of generic interface over containers, and then use that interface to provide what you thought was easier access to standard algorithms. But please correct me if I'm wrong. If not -if I'm right- what you did amounts to a crime in the eye of the STL police. Decoupling containers and algorithms is one of the greatest features of the STL, and going back on that -even if it involves some clever genericity- would buy you a direct ticket to the Java prison.



                          What you could have done instead



                          I understand that typing algorithm(std::begin(container), std::end(container); seems backwards when so many other languages have algorithm(container) or even container.algorithm. And I would very much agree that saving keystrokes is worth investing in the long term. Then simply take all those static private functions, and make them free and public, and put them into their own namespace.



                          namespace adapt {
                          template <typename Container, typename Compare = std::less<>>
                          void sort(Container& container, Compare cmp) {
                          std::sort(std::begin(container), std::end(container), cmp);
                          }
                          // ... and so on
                          }


                          Then if those toilsome keystrokes really bother you, it's only a matter of introducing the adapted function at the beginning of your file:



                          using adapt::lower_bound;
                          // ...
                          lower_bound(my_vec, my_val);


                          Or, if you want to be very clever, and aren't afraid of evil hacks and macros, you can write a generic adaptor for algorithms. It could be a lot cleaner if there were a standard way to turn generic functions into generic lambdas (because the latter can be deduced whereas the former can't), but it isn't that ugly either:



                          #include <iostream>
                          #include <algorithm>
                          #include <vector>

                          #define AS_LAMBDA(fn) (auto&&... args) { return fn(std::forward<decltype(args)>(args)...); }

                          #define CONTAINER_APPLY(algorithm, container, ...)
                          container_apply_impl(AS_LAMBDA(algorithm), container, __VA_ARGS__)

                          template <typename Algorithm, typename Container, typename... Args>
                          auto container_apply_impl(Algorithm algorithm, Container& container, Args&&... args) {
                          return algorithm(std::begin(container), std::end(container), std::forward<Args>(args)...);
                          }

                          int main() {
                          std::vector<int> vec{1, 2, 3, 4, 8, 7, 2, 8, 6, 4, 2, 3 };
                          CONTAINER_APPLY(std::for_each, vec, (auto i) { std::cout << i << ", "; });
                          }






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Oct 9 at 13:59









                          papagaga

                          3,989221




                          3,989221






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Code Review Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              Use MathJax to format equations. MathJax reference.


                              To learn more, see our tips on writing great answers.





                              Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                              Please pay close attention to the following guidance:


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f205029%2fgeneric-multiskiplist-adapter-class%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Morgemoulin

                              Scott Moir

                              Souastre