AK: Make Vector more const-correct by using RemoveReference<T>

Methods marked as const should always only return Foo const&, never
Foo&. Previously, this only worked correctly with Foo, but not with
Foo&: If someone fetched a T const&, this would have been expanded
to Foo& const& which is just Foo&. Therefore, they were able to modify
the elements of the Vector, even though it was marked as const.
This commit fixes that by introducing VisibleType as an alias for
RemoveReference<T> and using it throughout Vector.

There are also other cases where we don't require a mutable reference
to the underlying type, only a const reference (for example in a find
operation). In these cases, we now also correctly expand the type
to Foo const&.
This commit is contained in:
creator1creeper1 2022-01-08 23:46:38 +01:00 committed by Ali Mohammad Pur
parent 19d9d5bfe1
commit 6484d42933

View file

@ -46,6 +46,8 @@ private:
static constexpr bool contains_reference = IsLvalueReference<T>;
using StorageType = Conditional<contains_reference, RawPtr<RemoveReference<T>>, T>;
using VisibleType = RemoveReference<T>;
template<typename U>
static constexpr bool CanBePlacedInsideVector = Detail::CanBePlacedInsideVectorHelper<StorageType, contains_reference>::template value<U>;
@ -125,7 +127,7 @@ public:
return m_outline_buffer;
}
ALWAYS_INLINE T const& at(size_t i) const
ALWAYS_INLINE VisibleType const& at(size_t i) const
{
VERIFY(i < m_size);
if constexpr (contains_reference)
@ -134,7 +136,7 @@ public:
return data()[i];
}
ALWAYS_INLINE T& at(size_t i)
ALWAYS_INLINE VisibleType& at(size_t i)
{
VERIFY(i < m_size);
if constexpr (contains_reference)
@ -143,17 +145,17 @@ public:
return data()[i];
}
ALWAYS_INLINE T const& operator[](size_t i) const { return at(i); }
ALWAYS_INLINE T& operator[](size_t i) { return at(i); }
ALWAYS_INLINE VisibleType const& operator[](size_t i) const { return at(i); }
ALWAYS_INLINE VisibleType& operator[](size_t i) { return at(i); }
T const& first() const { return at(0); }
T& first() { return at(0); }
VisibleType const& first() const { return at(0); }
VisibleType& first() { return at(0); }
T const& last() const { return at(size() - 1); }
T& last() { return at(size() - 1); }
VisibleType const& last() const { return at(size() - 1); }
VisibleType& last() { return at(size() - 1); }
template<typename TUnaryPredicate>
Optional<T> first_matching(TUnaryPredicate predicate)
Optional<VisibleType> first_matching(TUnaryPredicate predicate) requires(!contains_reference)
{
for (size_t i = 0; i < size(); ++i) {
if (predicate(at(i))) {
@ -164,7 +166,7 @@ public:
}
template<typename TUnaryPredicate>
Optional<T> last_matching(TUnaryPredicate predicate)
Optional<VisibleType> last_matching(TUnaryPredicate predicate) requires(!contains_reference)
{
for (ssize_t i = size() - 1; i >= 0; --i) {
if (predicate(at(i))) {
@ -182,21 +184,21 @@ public:
return TypedTransfer<StorageType>::compare(data(), other.data(), size());
}
bool contains_slow(T const& value) const
bool contains_slow(VisibleType const& value) const
{
for (size_t i = 0; i < size(); ++i) {
if (Traits<T>::equals(at(i), value))
if (Traits<VisibleType>::equals(at(i), value))
return true;
}
return false;
}
bool contains_in_range(T const& value, size_t const start, size_t const end) const
bool contains_in_range(VisibleType const& value, size_t const start, size_t const end) const
{
VERIFY(start <= end);
VERIFY(end < size());
for (size_t i = start; i <= end; ++i) {
if (Traits<T>::equals(at(i), value))
if (Traits<VisibleType>::equals(at(i), value))
return true;
}
return false;
@ -689,8 +691,8 @@ public:
MUST(try_resize_and_keep_capacity(new_size));
}
using ConstIterator = SimpleIterator<Vector const, T const>;
using Iterator = SimpleIterator<Vector, T>;
using ConstIterator = SimpleIterator<Vector const, VisibleType const>;
using Iterator = SimpleIterator<Vector, VisibleType>;
ConstIterator begin() const { return ConstIterator::begin(*this); }
Iterator begin() { return Iterator::begin(*this); }
@ -710,17 +712,17 @@ public:
return AK::find_if(begin(), end(), forward<TUnaryPredicate>(finder));
}
ConstIterator find(T const& value) const
ConstIterator find(VisibleType const& value) const
{
return AK::find(begin(), end(), value);
}
Iterator find(T const& value)
Iterator find(VisibleType const& value)
{
return AK::find(begin(), end(), value);
}
Optional<size_t> find_first_index(T const& value) const
Optional<size_t> find_first_index(VisibleType const& value) const
{
if (auto const index = AK::find_index(begin(), end(), value);
index < size()) {