Fix KLinkedList; make it work even with strict aliasing

This commit is contained in:
TuxSH 2018-11-02 16:22:32 +01:00 committed by Michael Scire
parent 4078c9a07d
commit cfeebbd1c9

View file

@ -6,6 +6,7 @@
#include <initializer_list> #include <initializer_list>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <mesosphere/core/util.h>
#include <mesosphere/interfaces/ISlabAllocated.hpp> #include <mesosphere/interfaces/ISlabAllocated.hpp>
namespace mesosphere namespace mesosphere
@ -15,31 +16,50 @@ template<typename T>
class KLinkedList final { class KLinkedList final {
private: private:
struct List {
Node *last; struct List final {
Node *first; struct Node final : public ISlabAllocated<Node> {
size_t size = 0;
struct Link final {
Link *prev = nullptr;
Link *next = nullptr;
Node &parent()
{
return *detail::GetParentFromMember(this, &Node::link);
}
const Node &parent() const
{
return *detail::GetParentFromMember(this, &Node::link);
}
T &data() { return parent().data; }
const T &data() const { return parent().data; }
}; };
List list; Link link{};
struct Node final : public ISlabAllocated<Node> {
//friend class KLinkedList;
Node *prev = nullptr;
Node *next = nullptr;
T data{}; T data{};
Node() = default; Node() = default;
Node(const Node &other) = default; Node(const Node &other) = default;
Node(Node &&other) = default; Node(Node &&other) = default;
explicit Node(const T &data) : Node(), data{other} {} explicit Node(const T &data) : ISlabAllocated<Node>(), data{data} {}
explicit Node(const T &&data) : Node(), data{other} {} explicit Node(const T &&data) : ISlabAllocated<Node>(), data{data} {}
template<typename ...Args> template<typename ...Args>
explicit Node(Args&& ...args) : Node(), data{std::forward<Args>(args)...} {} explicit Node(Args&& ...args) : ISlabAllocated<Node>(), data{std::forward<Args>(args)...} {}
}; };
mutable typename Node::Link link{};
typename Node::Link *last() const { return link.prev; }
typename Node::Link *first() const { return link.next; }
void insert_node_after(Node *pos, Node *nd) size_t size = 0;
};
List list;
void insert_node_after(typename List::Node::Link *pos, typename List::Node::Link *nd)
{ {
// if pos is last & list is empty, ->next writes to first, etc. // if pos is last & list is empty, ->next writes to first, etc.
pos->next->prev = nd; pos->next->prev = nd;
@ -49,7 +69,7 @@ class KLinkedList final {
++list.size; ++list.size;
} }
void insert_node_before(Node *pos, Node *nd) void insert_node_before(typename List::Node::Link *pos, typename List::Node::Link *nd)
{ {
pos->prev->next = nd; pos->prev->next = nd;
nd->prev = pos->prev; nd->prev = pos->prev;
@ -58,7 +78,7 @@ class KLinkedList final {
++list.size; ++list.size;
} }
void remove_node(Node *nd) void remove_node(typename List::Node::Link *nd)
{ {
nd->prev->next = nd->next; nd->prev->next = nd->next;
nd->next->prev = nd->prev; nd->next->prev = nd->prev;
@ -95,12 +115,12 @@ class KLinkedList final {
reference operator*() reference operator*()
{ {
return node->data; return node->data();
} }
pointer operator->() pointer operator->()
{ {
return &node->data; return &node->data();
} }
Iterator &operator++() Iterator &operator++()
@ -135,13 +155,15 @@ class KLinkedList final {
private: private:
friend class KLinkedList; friend class KLinkedList;
KLinkedList::Node *node; typename KLinkedList::List::Node::Link *node;
explicit Iterator(KLinkedList::Node *node) : node{node} {} explicit Iterator(typename KLinkedList::List::Node::Link *node) : node{node} {}
}; };
using pointer = T *; using pointer = T *;
using const_pointer = const T *; using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using void_pointer = void *; using void_pointer = void *;
using const_void_ptr = const void *; using const_void_ptr = const void *;
using value_type = T; using value_type = T;
@ -152,41 +174,42 @@ class KLinkedList final {
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
KLinkedList() : list{(Node *)&list, (Node *)&list} {}; KLinkedList() : list{{&list.link, &list.link}} {};
explicit KLinkedList(size_t count, const T &value) : KLinkedList() explicit KLinkedList(size_t count, const T &value) : KLinkedList()
{ {
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
} }
explicit KLinkedList(size_t count) : KLinkedList() explicit KLinkedList(size_t count) : KLinkedList()
{ {
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
Node *nd = new Node; auto *nd = new typename List::Node;
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
} }
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>> template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
explicit KLinkedList(InputIt first, InputIt last) : KLinkedList() KLinkedList(InputIt first, InputIt last) : KLinkedList()
{ {
for (InputIt it = first; it != last; ++it) { for (InputIt it = first; it != last; ++it) {
Node *nd = new Node{*it}; auto *nd = new typename List::Node{*it};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
} }
void swap(KLinkedList &other) noexcept void swap(KLinkedList &other) noexcept
{ {
using std::swap; // Enable ADL using std::swap; // Enable ADL
swap(list.first, other.list.first); swap(list.link, other.list.link);
swap(list.last, other.list.last);
swap(list.size, other.list.size); swap(list.size, other.list.size);
list.first()->prev = list.last()->next = &list.link;
other.list.first()->prev = other.list.last()->next = &other.list.link;
} }
KLinkedList(KLinkedList &&other) noexcept : KLinkedList() KLinkedList(KLinkedList &&other) noexcept : KLinkedList()
@ -197,10 +220,10 @@ class KLinkedList final {
void clear() noexcept void clear() noexcept
{ {
Node *nxt; typename List::Node::Link *nxt;
for (Node nd = list.first; nd != (Node *)&list; nd = nxt) { for (typename List::Node::Link *nd = list.first(); nd != &list.link; nd = nxt) {
nxt = nd->next; nxt = nd->next;
delete nd; delete &nd->parent();
} }
list.size = 0; list.size = 0;
} }
@ -212,59 +235,59 @@ class KLinkedList final {
KLinkedList &operator=(KLinkedList other) KLinkedList &operator=(KLinkedList other)
{ {
swap(*this, other); swap(other);
return *this; return *this;
} }
KLinkedList &operator=(std::initializer_list<T> ilist) KLinkedList &operator=(std::initializer_list<T> ilist)
{ {
KLinkedList tmp{ilist}; KLinkedList tmp{ilist};
swap(*this, tmp); swap(tmp);
return *this; return *this;
} }
void assign(size_t count, const T &value) void assign(size_t count, const T &value)
{ {
KLinkedList tmp{count, value}; KLinkedList tmp{count, value};
swap(*this, tmp); swap(tmp);
} }
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>> template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
void assign(InputIt first, InputIt last) void assign(InputIt first, InputIt last)
{ {
KLinkedList tmp{first, last}; KLinkedList tmp{first, last};
swap(*this, tmp); swap(tmp);
} }
void assign(std::initializer_list<T> ilist) void assign(std::initializer_list<T> ilist)
{ {
KLinkedList tmp{ilist}; KLinkedList tmp{ilist};
swap(*this, tmp); swap(tmp);
} }
T &front() { return list.first->data; } T &front() { return list.first()->data(); }
const T &front() const { return list.first->data; } const T &front() const { return list.first()->data(); }
T &back() { return list.last->data; } T &back() { return list.last()->data(); }
const T &back(); const { return list.last->data; } const T &back() const { return list.last()->data(); }
const_iterator cbegin() const noexcept { return const_iterator{list.first}; } const_iterator cbegin() const noexcept { return const_iterator{list.first()}; }
const_iterator cend() const noexcept { return const_iterator{(Node *)&list}; } const_iterator cend() const noexcept { return const_iterator{&list.link}; }
const_iterator begin() const noexcept { return cbegin(); } const_iterator begin() const noexcept { return cbegin(); }
const_iterator end() const noexcept { return cend(); } const_iterator end() const noexcept { return cend(); }
iterator begin() noexcept { return iterator{list.first}; } iterator begin() noexcept { return iterator{list.first()}; }
iterator end() noexcept { return iterator{(Node *)&list}; } iterator end() noexcept { return iterator{&list.link}; }
const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{list.last}; } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{list.last()}; }
const_reverse_iterator crend() const noexcept { return const_reverse_iterator{(Node *)&list}; } const_reverse_iterator crend() const noexcept { return const_reverse_iterator{&list.link}; }
const_reverse_iterator rbegin() const noexcept { return crbegin(); } const_reverse_iterator rbegin() const noexcept { return crbegin(); }
const_reverse_iterator rend() const noexcept { return crend(); } const_reverse_iterator rend() const noexcept { return crend(); }
reverse_iterator rbegin() const noexcept { return reverse_iterator{list.last}; } reverse_iterator rbegin() noexcept { return reverse_iterator{list.last()}; }
reverse_iterator rend() const noexcept { return reverse_iterator{(Node *)&list}; } reverse_iterator rend() noexcept { return reverse_iterator{&list.link}; }
KLinkedList(const KLinkedList &other) : KLinkedList(other.cbegin(), other.cend()) {} KLinkedList(const KLinkedList &other) : KLinkedList(other.cbegin(), other.cend()) {}
@ -273,17 +296,17 @@ class KLinkedList final {
iterator insert(const_iterator pos, const T &value) iterator insert(const_iterator pos, const T &value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(pos.node, nd); insert_node_after(pos.node, &nd->link);
} }
iterator insert(const_iterator pos, T &&value) iterator insert(const_iterator pos, T &&value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_before(pos.node, nd); insert_node_before(pos.node, &nd->link);
return iterator{nd}; return iterator{&nd->link};
} }
iterator insert(const_iterator pos, size_t count, const T &value) iterator insert(const_iterator pos, size_t count, const T &value)
@ -299,33 +322,38 @@ class KLinkedList final {
iterator insert(const_iterator pos, InputIt first, InputIt last) iterator insert(const_iterator pos, InputIt first, InputIt last)
{ {
// Note: our list definition allows --begin() to be well defined // Note: our list definition allows --begin() to be well defined
Node *f = nullptr; typename List::Node::Link *f = nullptr;
Node *p = pos.node->prev; typename List::Node::Link *p = pos.node->prev;
for(InputIt it = first; it != last; ++it) { for(InputIt it = first; it != last; ++it) {
Node *nd = new Node{value}; auto *nd = new typename List::Node{*it};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(p, nd); insert_node_after(p, &nd->link);
p = nd; p = &nd->link;
f = f == nullptr ? nd : f; f = f == nullptr ? p : f;
} }
return iterator{f}; return iterator{f};
} }
iterator insert(const_iterator pos, std::initializer_list<T> ilist)
{
return insert(pos, ilist.begin(), ilist.end());
}
template<typename ...Args> template<typename ...Args>
iterator emplace(const_iterator pos, Args &&...args) iterator emplace(const_iterator pos, Args &&...args)
{ {
Node *nd = new Node{std::forward<Args>(args)...}; auto *nd = new typename List::Node{std::forward<Args>(args)...};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_before(pos.node, nd); insert_node_before(pos.node, &nd->link);
return iterator{nd}; return iterator{&nd->link};
} }
iterator erase(const_iterator pos) iterator erase(const_iterator pos)
{ {
iterator ret{pos->next}; iterator ret{pos.node->next};
remove_node(pos.node); remove_node(pos.node);
delete pos.node; delete &pos.node->parent();
return ret; return ret;
} }
@ -340,59 +368,62 @@ class KLinkedList final {
void push_back(const T &value) void push_back(const T &value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
void push_back(T &&value) void push_back(T &&value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
template< class... Args> template< class... Args>
T &emplace_back(Args&&... args) T &emplace_back(Args&&... args)
{ {
Node *nd = new Node{std::forward<Args>(args)...}; auto *nd = new typename List::Node{std::forward<Args>(args)...};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
return nd->data; return nd->data;
} }
void pop_back() void pop_back()
{ {
remove_node(list.last); auto *nd = list.last();
delete list.last; remove_node(nd);
delete &nd->parent();
} }
void push_front(const T &value) void push_front(const T &value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_before(list.first, nd); insert_node_before(list.first(), &nd->link);
} }
void push_front(T &&value) void push_front(T &&value)
{ {
Node *nd = new Node{value}; auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_before(list.first, nd); insert_node_before(list.first(), &nd->link);
} }
template< class... Args> template< class... Args>
T &emplace_front(Args&&... args) T &emplace_front(Args&&... args)
{ {
Node *nd = new Node{std::forward<Args>(args)...}; auto *nd = new typename List::Node{std::forward<Args>(args)...};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_before(list.first, nd); insert_node_before(list.first(), &nd->link);
return nd->data; return nd->data;
} }
void pop_front() void pop_front()
{ {
remove_node(list.first); auto *nd = list.first();
remove_node(nd);
delete &nd->parent();
} }
void resize(size_t count) void resize(size_t count)
@ -402,10 +433,11 @@ class KLinkedList final {
pop_back(); pop_back();
} }
} else { } else {
for (size_t i = 0; i < count - list.size; i++) { size_t s = list.size;
Node *nd = new Node; for (size_t i = 0; i < count - s; i++) {
auto *nd = new typename List::Node;
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
} }
} }
@ -417,26 +449,33 @@ class KLinkedList final {
pop_back(); pop_back();
} }
} else { } else {
for (size_t i = 0; i < count - list.size; i++) { size_t s = list.size;
Node *nd = new Node{value}; for (size_t i = 0; i < count - s; i++) {
auto *nd = new typename List::Node{value};
kassert(nd != nullptr); kassert(nd != nullptr);
insert_node_after(list.last, nd); insert_node_after(list.last(), &nd->link);
} }
} }
} }
void splice(const_iterator pos, KLinkedList &&other) void splice(const_iterator pos, KLinkedList &&other)
{ {
Node *before = pos.node->prev; //auto *current = pos.node;
Node *after = pos.node->next; auto *before = pos.node->prev;
before->next = other.list.first; auto *after = pos.node; //current == &list.link ? current : pos.node->next;
before->next = other.list.first();
before->next->prev = before; before->next->prev = before;
after->prev = other.list.last; after->prev = other.list.last();
after->prev->next = after; after->prev->next = after;
list.size += other.list.size; list.size += other.list.size;
other.list.size = 0; other.list.size = 0;
other.list.first = other.list.last = (Node *)&other.list; other.list.link.prev = other.list.link.next = &other.list.link;
}
void splice(const_iterator pos, KLinkedList &other)
{
splice(pos, std::move(other));
} }
void splice(const_iterator pos, KLinkedList &&other, const_iterator it) void splice(const_iterator pos, KLinkedList &&other, const_iterator it)
@ -445,11 +484,17 @@ class KLinkedList final {
insert_node_before(pos.node, it.node); insert_node_before(pos.node, it.node);
} }
void splice(const_iterator pos, KLinkedList &other, const_iterator it)
{
splice(pos, std::move(other), it);
}
void splice(const_iterator pos, KLinkedList &&other, const_iterator first, const_iterator last) void splice(const_iterator pos, KLinkedList &&other, const_iterator first, const_iterator last)
{ {
if (*this == other) { if (*this == other) {
Node *before = pos.node->prev; auto *current = pos.node;
Node *after = pos.node->next; auto *before = pos.node->prev;
auto *after = current == &list.link ? current : pos.node->next;
before->next = first.node; before->next = first.node;
before->next->prev = before; before->next->prev = before;
after->prev = last.node; after->prev = last.node;
@ -466,6 +511,11 @@ class KLinkedList final {
} }
} }
void splice(const_iterator pos, KLinkedList &other, const_iterator first, const_iterator last)
{
splice(pos, std::move(other), first, last);
}
size_t remove(const T &value) size_t remove(const T &value)
{ {
size_t n = 0; size_t n = 0;
@ -485,6 +535,7 @@ class KLinkedList final {
size_t remove_if(UnaryPredicate p) size_t remove_if(UnaryPredicate p)
{ {
const_iterator next; const_iterator next;
size_t n = 0;
for (const_iterator it = cbegin(); it != cend(); ) { for (const_iterator it = cbegin(); it != cend(); ) {
if (p(*it)) { if (p(*it)) {
it = erase(it); it = erase(it);
@ -497,18 +548,18 @@ class KLinkedList final {
} }
template<typename Compare> template<typename Compare>
void merge(KLinkedList &other, Compare p) void merge(KLinkedList &&other, Compare p)
{ {
Node hd{}; typename List::Node::Link hd{};
Node *cur = &hd; auto *cur = &hd;
size_t n = 0; size_t n = 0;
while (list.size > 0 && other.list.size > 0) { while (list.size > 0 && other.list.size > 0) {
if (p(list.first, other.list.first)) { if (p(list.first()->data(), other.list.first()->data())) {
cur->next = list.first; cur->next = list.first();
remove_node(list.first); remove_node(list.first());
} else { } else {
cur->next = other.list.first; cur->next = other.list.first();
other.remove_node(other.list.first); other.remove_node(other.list.first());
} }
cur->next->prev = cur; cur->next->prev = cur;
cur = cur->next; cur = cur->next;
@ -517,75 +568,77 @@ class KLinkedList final {
// Steal the remaining elements // Steal the remaining elements
if (list.size > 0) { if (list.size > 0) {
cur->next = list.first; cur->next = list.first();
list.first->prev = cur; list.first()->prev = cur;
cur = list.last();
n += list.size; n += list.size;
} else if (other.list.size > 0) { } else if (other.list.size > 0) {
cur->next = other.list.first; cur->next = other.list.first();
other.list.first->prev = cur; other.list.first()->prev = cur;
cur = other.list.last();
n += other.list.size; n += other.list.size;
} }
// Reset the other list to put it in a valid state // Reset the other list to put it in a valid state
other.list.first = other.list.last = (Node *)other.list; other.list.link.prev = other.list.link.next = &other.list.link;
other.list.size = 0; other.list.size = 0;
// Finally, normalize the result and assign it to this // Finally, normalize the result and assign it to this
list.first = hd.next; list.link.next = hd.next;
list.last = cur; list.link.prev = cur;
list.size = n; list.size = n;
list.first->prev = list.last->next = (Node *)list; list.first()->prev = list.last()->next = &list.link;
}
void merge(KLinkedList &other)
{
merge(other, std::less{});
}
template<typename Compare>
void merge(KLinkedList &&other, Compare p)
{
KLinkedList &o = other;
merge(o, p);
} }
void merge(KLinkedList &&other) void merge(KLinkedList &&other)
{ {
merge(other, std::less{}); merge(other, std::less<T>{});
}
template<typename Compare>
void merge(KLinkedList &other, Compare p)
{
merge(std::move(other), p);
}
void merge(KLinkedList &other)
{
merge(other, std::less<T>{});
} }
void reverse() noexcept void reverse() noexcept
{ {
Node hd{}; typename List::Node::Link hd{};
Node *cur = &hd; auto *cur = &hd;
size_t n = 0; size_t n = 0;
while (list.size > 0) { while (list.size > 0) {
cur->next = list.last; cur->next = list.last();
remove_node(list.last); remove_node(list.last());
cur->next->prev = cur; cur->next->prev = cur;
n++; n++;
cur = cur->next; cur = cur->next;
} }
list.first = hd.next; list.link.next = hd.next;
list.last = cur; list.link.prev = cur;
list.size = n; list.size = n;
list.first->prev = list.last->next = (Node *)list; list.first()->prev = list.last()->next = &list.link;
} }
template<typename BinaryPredicate> template<typename BinaryPredicate>
size_t unique(BinaryPredicate p) size_t unique(BinaryPredicate p)
{ {
Node *nxt; typename List::Node::Link *nxt;
size_t n = 0; size_t n = 0;
for (Node *nd = list.first; nd != (Node *)list; nd = nxt) { for (auto *nd = list.first(); nd != &list.link; nd = nxt) {
nxt = nd->next; nxt = nd->next;
for (Node *nd2 = nxt; nd2 != (Node *)list && p(nd->data, nd2->data); nd2 = nxt) { for (auto *nd2 = nxt; nd2 != &list.link && p(nd->data(), nd2->data()); nd2 = nxt) {
nxt = nd2->next; nxt = nd2->next;
remove_node(nd2); remove_node(nd2);
delete nd2; delete &nd2->parent();
++n; ++n;
} }
} }
@ -595,7 +648,7 @@ class KLinkedList final {
size_t unique() size_t unique()
{ {
return unique(std::equal_to); return unique(std::equal_to<T>{});
} }
// sort: a PITA to implement and not needed anyway // sort: a PITA to implement and not needed anyway
@ -612,22 +665,22 @@ class KLinkedList final {
friend bool operator<(const KLinkedList &lhs, const KLinkedList &rhs) friend bool operator<(const KLinkedList &lhs, const KLinkedList &rhs)
{ {
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less{}); return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less<T>{});
} }
friend bool operator>(const KLinkedList &lhs, const KLinkedList &rhs) friend bool operator>(const KLinkedList &lhs, const KLinkedList &rhs)
{ {
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater{}); return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater<T>{});
} }
friend bool operator<=(const KLinkedList &lhs, const KLinkedList &rhs) friend bool operator<=(const KLinkedList &lhs, const KLinkedList &rhs)
{ {
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less_equal{}); return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less_equal<T>{});
} }
friend bool operator>=(const KLinkedList &lhs, const KLinkedList &rhs) friend bool operator>=(const KLinkedList &lhs, const KLinkedList &rhs)
{ {
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater_equal{}); return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater_equal<T>{});
} }
friend void swap(KLinkedList &lhs, KLinkedList &rhs) friend void swap(KLinkedList &lhs, KLinkedList &rhs)
@ -637,4 +690,7 @@ class KLinkedList final {
}; };
template<typename InputIt>
KLinkedList(InputIt b, InputIt e) -> KLinkedList<typename std::iterator_traits<InputIt>::value_type>;
} }