Merge pull request #3516 from sfc-gh-anoyes/anoyes/vector-tests
Add unit tests for SmallVectorRef and VectorRef
This commit is contained in:
commit
50a478b5d0
157
flow/Arena.cpp
157
flow/Arena.cpp
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
#include "Arena.h"
|
#include "Arena.h"
|
||||||
|
|
||||||
|
#include "flow/UnitTest.h"
|
||||||
|
|
||||||
// See https://dox.ipxe.org/memcheck_8h_source.html and https://dox.ipxe.org/valgrind_8h_source.html for an explanation
|
// See https://dox.ipxe.org/memcheck_8h_source.html and https://dox.ipxe.org/valgrind_8h_source.html for an explanation
|
||||||
// of valgrind client requests
|
// of valgrind client requests
|
||||||
#ifdef VALGRIND_ARENA
|
#ifdef VALGRIND_ARENA
|
||||||
|
@ -388,3 +390,158 @@ void ArenaBlock::destroyLeaf() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testRangeBasedForLoop() {
|
||||||
|
VectorRefLike<StringRef> xs;
|
||||||
|
Arena a;
|
||||||
|
int size = deterministicRandom()->randomInt(0, 100);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
xs.push_back_deep(a, StringRef(std::to_string(i)));
|
||||||
|
}
|
||||||
|
ASSERT(xs.size() == size);
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& x : xs) {
|
||||||
|
ASSERT(x == StringRef(std::to_string(i++)));
|
||||||
|
}
|
||||||
|
ASSERT(i == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testIteratorIncrement() {
|
||||||
|
VectorRefLike<StringRef> xs;
|
||||||
|
Arena a;
|
||||||
|
int size = deterministicRandom()->randomInt(0, 100);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
xs.push_back_deep(a, StringRef(std::to_string(i)));
|
||||||
|
}
|
||||||
|
ASSERT(xs.size() == size);
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto iter = xs.begin(); iter != xs.end();) {
|
||||||
|
ASSERT(*iter++ == StringRef(std::to_string(i++)));
|
||||||
|
}
|
||||||
|
ASSERT(i == size);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto iter = xs.begin(); iter != xs.end() && i < xs.size() - 1;) {
|
||||||
|
ASSERT(*++iter == StringRef(std::to_string(++i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto iter = xs.begin(); iter < xs.end();) {
|
||||||
|
ASSERT(*iter == StringRef(std::to_string(i)));
|
||||||
|
iter += 2;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int i = xs.size() - 1;
|
||||||
|
for (auto iter = xs.end() - 1; iter >= xs.begin();) {
|
||||||
|
ASSERT(*iter == StringRef(std::to_string(i)));
|
||||||
|
iter -= 2;
|
||||||
|
i -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto iter = xs.begin(); iter < xs.end();) {
|
||||||
|
ASSERT(*iter == StringRef(std::to_string(i)));
|
||||||
|
iter = iter + 2;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int i = xs.size() - 1;
|
||||||
|
for (auto iter = xs.end() - 1; iter >= xs.begin();) {
|
||||||
|
ASSERT(*iter == StringRef(std::to_string(i)));
|
||||||
|
iter = iter - 2;
|
||||||
|
i -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testReverseIterator() {
|
||||||
|
VectorRefLike<StringRef> xs;
|
||||||
|
Arena a;
|
||||||
|
int size = deterministicRandom()->randomInt(0, 100);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
xs.push_back_deep(a, StringRef(std::to_string(i)));
|
||||||
|
}
|
||||||
|
ASSERT(xs.size() == size);
|
||||||
|
|
||||||
|
int i = xs.size() - 1;
|
||||||
|
for (auto iter = xs.rbegin(); iter != xs.rend();) {
|
||||||
|
ASSERT(*iter++ == StringRef(std::to_string(i--)));
|
||||||
|
}
|
||||||
|
ASSERT(i == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testAppend() {
|
||||||
|
VectorRefLike<StringRef> xs;
|
||||||
|
Arena a;
|
||||||
|
int size = deterministicRandom()->randomInt(0, 100);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
xs.push_back_deep(a, StringRef(std::to_string(i)));
|
||||||
|
}
|
||||||
|
VectorRefLike<StringRef> ys;
|
||||||
|
ys.append(a, xs.begin(), xs.size());
|
||||||
|
ASSERT(xs.size() == ys.size());
|
||||||
|
ASSERT(std::equal(xs.begin(), xs.end(), ys.begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testCopy() {
|
||||||
|
Standalone<VectorRefLike<StringRef>> xs;
|
||||||
|
int size = deterministicRandom()->randomInt(0, 100);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
xs.push_back_deep(xs.arena(), StringRef(std::to_string(i)));
|
||||||
|
}
|
||||||
|
Arena a;
|
||||||
|
VectorRefLike<StringRef> ys(a, xs);
|
||||||
|
xs = Standalone<VectorRefLike<StringRef>>();
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& y : ys) {
|
||||||
|
ASSERT(y == StringRef(std::to_string(i++)));
|
||||||
|
}
|
||||||
|
ASSERT(i == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class> class VectorRefLike>
|
||||||
|
void testVectorLike() {
|
||||||
|
testRangeBasedForLoop<VectorRefLike>();
|
||||||
|
testIteratorIncrement<VectorRefLike>();
|
||||||
|
testReverseIterator<VectorRefLike>();
|
||||||
|
testAppend<VectorRefLike>();
|
||||||
|
testCopy<VectorRefLike>();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Fix number of template parameters
|
||||||
|
template <class T>
|
||||||
|
using VectorRefProxy = VectorRef<T>;
|
||||||
|
TEST_CASE("/flow/Arena/VectorRef") {
|
||||||
|
testVectorLike<VectorRefProxy>();
|
||||||
|
return Void();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix number of template parameters
|
||||||
|
template <class T>
|
||||||
|
using SmallVectorRefProxy = SmallVectorRef<T>;
|
||||||
|
TEST_CASE("/flow/Arena/SmallVectorRef") {
|
||||||
|
testVectorLike<SmallVectorRefProxy>();
|
||||||
|
return Void();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix number of template parameters
|
||||||
|
template <class T>
|
||||||
|
using SmallVectorRef10Proxy = SmallVectorRef<T, 10>;
|
||||||
|
TEST_CASE("/flow/Arena/SmallVectorRef10") {
|
||||||
|
testVectorLike<SmallVectorRef10Proxy>();
|
||||||
|
return Void();
|
||||||
|
}
|
||||||
|
|
208
flow/Arena.h
208
flow/Arena.h
|
@ -829,6 +829,15 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class It>
|
||||||
|
VectorRef(Arena& arena, It first, It last) {
|
||||||
|
if constexpr (flow_ref<T>::value) {
|
||||||
|
append_deep(arena, first, std::distance(first, last));
|
||||||
|
} else {
|
||||||
|
append(arena, first, std::distance(first, last));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VectorRef(T* data, int size) : data(data), m_size(size), m_capacity(size) {}
|
VectorRef(T* data, int size) : data(data), m_size(size), m_capacity(size) {}
|
||||||
VectorRef(T* data, int size, int capacity) : data(data), m_size(size), m_capacity(capacity) {}
|
VectorRef(T* data, int size, int capacity) : data(data), m_size(size), m_capacity(capacity) {}
|
||||||
// VectorRef( const VectorRef<T>& toCopy ) : data( toCopy.data ), m_size( toCopy.m_size ), m_capacity(
|
// VectorRef( const VectorRef<T>& toCopy ) : data( toCopy.data ), m_size( toCopy.m_size ), m_capacity(
|
||||||
|
@ -917,7 +926,8 @@ public:
|
||||||
VPS::add(*ptr);
|
VPS::add(*ptr);
|
||||||
m_size++;
|
m_size++;
|
||||||
}
|
}
|
||||||
void append(Arena& p, const T* begin, int count) {
|
template <class It>
|
||||||
|
void append(Arena& p, It begin, int count) {
|
||||||
if (m_size + count > m_capacity) reallocate(p, m_size + count);
|
if (m_size + count > m_capacity) reallocate(p, m_size + count);
|
||||||
VPS::invalidate();
|
VPS::invalidate();
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -1002,6 +1012,8 @@ protected:
|
||||||
// won't need allocations in these cases.
|
// won't need allocations in these cases.
|
||||||
template <class T, int InlineMembers = 1>
|
template <class T, int InlineMembers = 1>
|
||||||
class SmallVectorRef {
|
class SmallVectorRef {
|
||||||
|
static_assert(InlineMembers >= 0);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// types
|
// types
|
||||||
template <bool isConst>
|
template <bool isConst>
|
||||||
|
@ -1012,28 +1024,52 @@ public:
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Category = std::random_access_iterator_tag;
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
using value_type = std::conditional_t<isConst, const T, T>;
|
using value_type = std::conditional_t<isConst, const T, T>;
|
||||||
using difference_type = int;
|
using difference_type = int;
|
||||||
using pointer = value_type*;
|
using pointer = value_type*;
|
||||||
using reference = value_type&;
|
using reference = value_type&;
|
||||||
friend class SmallVectorRef<T, InlineMembers>;
|
friend class SmallVectorRef<T, InlineMembers>;
|
||||||
template <bool I>
|
friend bool operator<(const self_t& lhs, const self_t& rhs) {
|
||||||
friend bool operator<(const iterator_impl<I>&, const iterator_impl<I>&);
|
ASSERT(lhs.vec == rhs.vec);
|
||||||
template <bool I>
|
return lhs.idx < rhs.idx;
|
||||||
friend bool operator>(const iterator_impl<I>&, const iterator_impl<I>&);
|
}
|
||||||
template <bool I>
|
friend bool operator>(const self_t& lhs, const self_t& rhs) {
|
||||||
friend bool operator<=(const iterator_impl<I>&, const iterator_impl<I>&);
|
ASSERT(lhs.vec == rhs.vec);
|
||||||
template <bool I>
|
return lhs.idx > rhs.idx;
|
||||||
friend bool operator>=(const iterator_impl<I>&, const iterator_impl<I>&);
|
}
|
||||||
template <bool I>
|
friend bool operator<=(const self_t& lhs, const self_t& rhs) {
|
||||||
friend self_t operator+(const iterator_impl<I>&, difference_type);
|
ASSERT(lhs.vec == rhs.vec);
|
||||||
template <bool I>
|
return lhs.idx <= rhs.idx;
|
||||||
friend self_t operator+(difference_type, const self_t&);
|
}
|
||||||
template <bool I>
|
friend bool operator>=(const self_t& lhs, const self_t& rhs) {
|
||||||
friend self_t operator-(const iterator_impl<I>&, difference_type);
|
ASSERT(lhs.vec == rhs.vec);
|
||||||
template <bool I>
|
return lhs.idx >= rhs.idx;
|
||||||
friend difference_type operator-(iterator_impl<I>, self_t);
|
}
|
||||||
|
friend self_t operator+(const self_t& lhs, difference_type diff) {
|
||||||
|
auto res = lhs;
|
||||||
|
res.idx += diff;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
friend self_t operator+(difference_type diff, const self_t& lhs) {
|
||||||
|
auto res = lhs;
|
||||||
|
res.idx += diff;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
friend self_t operator-(const self_t& lhs, difference_type diff) {
|
||||||
|
auto res = lhs;
|
||||||
|
res.idx -= diff;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
friend self_t operator-(difference_type diff, const self_t& lhs) {
|
||||||
|
auto res = lhs;
|
||||||
|
res.idx -= diff;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
friend difference_type operator-(const self_t& lhs, const self_t& rhs) {
|
||||||
|
ASSERT(lhs.vec == rhs.vec);
|
||||||
|
return lhs.idx - rhs.idx;
|
||||||
|
}
|
||||||
|
|
||||||
self_t& operator++() {
|
self_t& operator++() {
|
||||||
++idx;
|
++idx;
|
||||||
|
@ -1068,7 +1104,7 @@ public:
|
||||||
if (i < InlineMembers) {
|
if (i < InlineMembers) {
|
||||||
return vec->arr[i];
|
return vec->arr[i];
|
||||||
} else {
|
} else {
|
||||||
return vec->data[i];
|
return vec->data[i - InlineMembers];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reference get() const { return get(idx); }
|
reference get() const { return get(idx); }
|
||||||
|
@ -1094,41 +1130,25 @@ public: // Construction
|
||||||
|
|
||||||
template <class T2 = T, int IM = InlineMembers>
|
template <class T2 = T, int IM = InlineMembers>
|
||||||
SmallVectorRef(Arena& arena, const SmallVectorRef<T, IM>& toCopy,
|
SmallVectorRef(Arena& arena, const SmallVectorRef<T, IM>& toCopy,
|
||||||
typename std::enable_if<!flow_ref<T2>::value, int>::type = 0)
|
typename std::enable_if<!flow_ref<T2>::value, int>::type = 0)
|
||||||
: m_size(toCopy.m_size) {
|
: m_size(0) {
|
||||||
if (toCopy.size() > InlineMembers) {
|
append(arena, toCopy.begin(), toCopy.size());
|
||||||
data.resize(arena, toCopy.size() - InlineMembers);
|
|
||||||
}
|
|
||||||
std::copy(toCopy.cbegin(), toCopy.cend(), begin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T2 = T, int IM = InlineMembers>
|
template <class T2 = T, int IM = InlineMembers>
|
||||||
SmallVectorRef(Arena& arena, const SmallVectorRef<T2, IM>& toCopy,
|
SmallVectorRef(Arena& arena, const SmallVectorRef<T2, IM>& toCopy,
|
||||||
typename std::enable_if<flow_ref<T2>::value, int>::type = 0)
|
typename std::enable_if<flow_ref<T2>::value, int>::type = 0)
|
||||||
: m_size(toCopy.m_size) {
|
: m_size(0) {
|
||||||
for (int i = 0; i < toCopy.size(); ++i) {
|
append_deep(arena, toCopy.begin(), toCopy.size());
|
||||||
if (i < arr.size()) {
|
|
||||||
new (&arr[i]) T(arena, toCopy[i]);
|
|
||||||
} else {
|
|
||||||
data.push_back_deep(arena, toCopy[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class It>
|
template <class It>
|
||||||
SmallVectorRef(Arena& arena, It first, It last)
|
SmallVectorRef(Arena& arena, It first, It last) : m_size(0) {
|
||||||
: m_size(0) {
|
if constexpr (flow_ref<T>::value) {
|
||||||
while (first != last && m_size < InlineMembers) {
|
append_deep(arena, first, std::distance(first, last));
|
||||||
new (&arr[m_size++]) T(*(first++));
|
} else {
|
||||||
|
append(arena, first, std::distance(first, last));
|
||||||
}
|
}
|
||||||
while (first != last) {
|
|
||||||
data.push_back(arena, *(first++));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SmallVectorRef(SmallVectorRef<T, InlineMembers>&& o)
|
|
||||||
: m_size(o.m_size), arr(std::move(o.arr)), data(std::move(o.data)) {
|
|
||||||
o.m_size = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public: // information
|
public: // information
|
||||||
|
@ -1171,37 +1191,25 @@ public: // Modification
|
||||||
void pop_back() {--m_size; }
|
void pop_back() {--m_size; }
|
||||||
|
|
||||||
template <class It>
|
template <class It>
|
||||||
void append(Arena& arena, It first, It last) {
|
void append(Arena& arena, It first, int count) {
|
||||||
if (first == last) {
|
ASSERT(count >= 0);
|
||||||
return;
|
while (count > 0 && m_size < InlineMembers) {
|
||||||
}
|
|
||||||
auto d = std::distance(first, last);
|
|
||||||
if (m_size + d > InlineMembers) {
|
|
||||||
data.reserve(arena, m_size + d - InlineMembers);
|
|
||||||
}
|
|
||||||
while (first != last && m_size < InlineMembers) {
|
|
||||||
new (&(arr[m_size++])) T(*(first++));
|
new (&(arr[m_size++])) T(*(first++));
|
||||||
|
--count;
|
||||||
}
|
}
|
||||||
while (first != last) {
|
data.append(arena, first, count);
|
||||||
data.push_back(arena, *(first++));
|
m_size += count;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class It>
|
template <class It>
|
||||||
void append_deep(Arena& arena, It first, It last) {
|
void append_deep(Arena& arena, It first, int count) {
|
||||||
if (first == last) {
|
ASSERT(count >= 0);
|
||||||
return;
|
while (count > 0 && m_size < InlineMembers) {
|
||||||
}
|
new (&(arr[m_size++])) T(*(first++));
|
||||||
auto d = std::distance(first, last);
|
--count;
|
||||||
if (m_size + d > InlineMembers) {
|
|
||||||
data.reserve(arena, m_size + d - InlineMembers);
|
|
||||||
}
|
|
||||||
while (first != last && m_size < InlineMembers) {
|
|
||||||
new (&(arr[m_size++])) T(arena, *(first++));
|
|
||||||
}
|
|
||||||
while (first != last) {
|
|
||||||
data.push_back_deep(arena, *(first++));
|
|
||||||
}
|
}
|
||||||
|
data.append_deep(arena, first, count);
|
||||||
|
m_size += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public: // iterator access
|
public: // iterator access
|
||||||
|
@ -1255,66 +1263,6 @@ private:
|
||||||
VectorRef<T> data;
|
VectorRef<T> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
bool operator<(const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& lhs,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& rhs) {
|
|
||||||
return lhs.idx < rhs.idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
bool operator>(const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& lhs,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& rhs) {
|
|
||||||
return lhs.idx > rhs.idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
bool operator<=(const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& lhs,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& rhs) {
|
|
||||||
return lhs.idx <= rhs.idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
bool operator>=(const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& lhs,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& rhs) {
|
|
||||||
return lhs.idx >= rhs.idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst> operator+(
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& iter,
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>::difference_type diff) {
|
|
||||||
auto res = iter;
|
|
||||||
res.idx += diff;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst> operator+(
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>::difference_type diff,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& iter) {
|
|
||||||
auto res = iter;
|
|
||||||
res.idx += diff;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst> operator-(
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& iter,
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>::difference_type diff) {
|
|
||||||
auto res = iter;
|
|
||||||
res.idx -= diff;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, int InlineMembers, bool isConst>
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst> operator-(
|
|
||||||
typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>::difference_type diff,
|
|
||||||
const typename SmallVectorRef<T, InlineMembers>::template iterator_impl<isConst>& iter) {
|
|
||||||
auto res = iter;
|
|
||||||
res.idx -= diff;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct Traceable<VectorRef<T>> {
|
struct Traceable<VectorRef<T>> {
|
||||||
constexpr static bool value = Traceable<T>::value;
|
constexpr static bool value = Traceable<T>::value;
|
||||||
|
|
Loading…
Reference in New Issue