Long live QSpan as public API!

Provide qspan_p.h as backward-compatibility header.

[ChangeLog][QtCore][QSpan] New Qt equivalent of std::span.

Fixes: QTBUG-115022
Change-Id: I1cc27dc0aa1f7406f0a41d7a75f176cd7f858feb
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2023-05-26 13:15:44 +02:00
parent 5885db33e3
commit 03e78e5d62
10 changed files with 1072 additions and 407 deletions

View File

@ -307,6 +307,7 @@ qt_internal_add_module(Core
tools/qsharedpointer.cpp tools/qsharedpointer.h
tools/qsharedpointer_impl.h
tools/qsize.cpp tools/qsize.h
tools/qspan.h
tools/qspan_p.h
tools/qstack.h
tools/qtaggedpointer.h

View File

@ -17,6 +17,7 @@
#include <qlist.h>
#include "qlocale.h"
#include "qlocale_p.h"
#include "qspan.h"
#include "qstringbuilder.h"
#include "qstringmatcher.h"
#include "qvarlengtharray.h"
@ -30,7 +31,6 @@
#endif
#include <private/qfunctions_p.h>
#include <private/qspan_p.h>
#include <limits.h>
#include <string.h>

View File

@ -12,6 +12,7 @@
#endif
// std headers can unfortunately not be forward declared
#include <cstddef> // std::size_t
#include <utility>
QT_BEGIN_NAMESPACE
@ -25,6 +26,7 @@ template <typename T1, typename T2>
using QPair = std::pair<T1, T2>;
template <typename T> class QQueue;
template <typename T> class QSet;
template <typename T, std::size_t E = std::size_t(-1) /* = std::dynamic_extent*/> class QSpan;
template <typename T> class QStack;
template <typename T, qsizetype Prealloc = 256> class QVarLengthArray;
template <typename T> class QList;

424
src/corelib/tools/qspan.h Normal file
View File

@ -0,0 +1,424 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSPAN_H
#define QSPAN_H
#include <QtCore/qcompilerdetection.h>
#include <QtCore/qtypes.h>
#include <QtCore/qcontainerfwd.h>
#include <array>
#include <cstddef>
#include <cassert>
#include <QtCore/q20iterator.h>
#include <QtCore/q20memory.h>
#ifdef __cpp_lib_span
#include <span>
#endif
#include <QtCore/q20type_traits.h>
QT_BEGIN_NAMESPACE
// like std::dynamic_extent
namespace q20 {
inline constexpr auto dynamic_extent = std::size_t(-1);
} // namespace q20
QT_BEGIN_INCLUDE_NAMESPACE
#ifdef __cpp_lib_span
#ifdef __cpp_lib_concepts
namespace std::ranges {
// Officially, these are defined in <ranges>, but that is a heavy-hitter header.
// OTOH, <span> must specialize these variable templates, too, so we assume that
// <span> includes some meaningful subset of <ranges> and just go ahead and use them:
template <typename T, std::size_t E>
constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
template <typename T, std::size_t E>
constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
} // namespace std::ranges
#endif // __cpp_lib_concepts
#endif // __cpp_lib_span
QT_END_INCLUDE_NAMESPACE
namespace QSpanPrivate {
template <typename T, std::size_t E> class QSpanBase;
template <typename T>
struct is_qspan_helper : std::false_type {};
template <typename T, std::size_t E>
struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
template <typename T, std::size_t E>
struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
template <typename T>
using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
template <typename T>
struct is_std_span_helper : std::false_type {};
#ifdef __cpp_lib_span
template <typename T, std::size_t E>
struct is_std_span_helper<std::span<T, E>> : std::true_type {};
#endif // __cpp_lib_span
template <typename T>
using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
template <typename T>
struct is_std_array_helper : std::false_type {};
template <typename T, std::size_t N>
struct is_std_array_helper<std::array<T, N>> : std::true_type {};
template <typename T>
using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
template <typename From, typename To>
using is_qualification_conversion =
std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1
template <typename From, typename To>
constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
// not doing the extra work C++20 requires
template <typename Range>
decltype(auto) adl_begin(Range &&r) { using std::begin; return begin(r); }
template <typename Range>
decltype(auto) adl_data(Range &&r) { using std::data; return data(r); }
template <typename Range>
decltype(auto) adl_size(Range &&r) { using std::size; return size(r); }
// Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin)
// This one uses adl_begin() instead.
template <typename Range>
using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
template <typename Range>
using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
template <typename T>
class QSpanCommon {
protected:
template <typename Iterator>
using is_compatible_iterator = std::conjunction<
std::is_base_of<
std::random_access_iterator_tag,
typename std::iterator_traits<Iterator>::iterator_category
>,
is_qualification_conversion<
std::remove_reference_t<q20::iter_reference_t<Iterator>>,
T
>
>;
template <typename Iterator, typename End>
using is_compatible_iterator_and_sentinel = std::conjunction<
is_compatible_iterator<Iterator>,
std::negation<std::is_convertible<End, std::size_t>>
>;
template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t:
struct is_compatible_range_helper : std::false_type {};
template <typename Range>
struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
: is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
template <typename Range>
using is_compatible_range = std::conjunction<
// ### this needs more work, esp. extension to C++20 contiguous iterators
std::negation<is_qspan<Range>>,
std::negation<is_std_span<Range>>,
std::negation<is_std_array<Range>>,
std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
is_compatible_range_helper<Range>
>;
// constraints
template <typename Iterator>
using if_compatible_iterator = std::enable_if_t<
is_compatible_iterator<Iterator>::value
, bool>;
template <typename Iterator, typename End>
using if_compatible_iterator_and_sentinel = std::enable_if_t<
is_compatible_iterator_and_sentinel<Iterator, End>::value
, bool>;
template <typename Range>
using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
}; // class QSpanCommon
template <typename T, std::size_t E>
class QSpanBase : protected QSpanCommon<T>
{
static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
"QSpan only supports extents that fit into the signed size type (qsizetype).");
struct Enabled_t { explicit Enabled_t() = default; };
static inline constexpr Enabled_t Enable{};
template <typename S, std::size_t N>
using if_compatible_array = std::enable_if_t<
N == E && is_qualification_conversion_v<S, T>
, bool>;
template <typename S>
using if_qualification_conversion = std::enable_if_t<
is_qualification_conversion_v<S, T>
, bool>;
protected:
using Base = QSpanCommon<T>;
// data members:
T *m_data;
static constexpr qsizetype m_size = qsizetype(E);
// types and constants:
// (in QSpan only)
// constructors (need to be public d/t the way ctor inheriting works):
public:
template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
template <typename It, typename Base::template if_compatible_iterator<It> = true>
explicit constexpr QSpanBase(It first, qsizetype count)
: m_data{q20::to_address(first)}
{
Q_ASSERT(count == m_size);
}
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
explicit constexpr QSpanBase(It first, End last)
: QSpanBase(first, last - first) {}
template <size_t N, std::enable_if_t<N == E, bool> = true>
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
: QSpanBase(arr, N) {}
template <typename S, size_t N, if_compatible_array<S, N> = true>
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename S, size_t N, if_compatible_array<S, N> = true>
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename Range, typename Base::template if_compatible_range<Range> = true>
Q_IMPLICIT constexpr QSpanBase(Range &&r)
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
: QSpanBase(other.data(), other.size())
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
: QSpanBase(other.data(), other.size())
{}
#ifdef __cpp_lib_span
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
: QSpanBase(other.data(), other.size())
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
: QSpanBase(other.data(), other.size())
{}
#endif // __cpp_lib_span
}; // class QSpanBase (fixed extent)
template <typename T>
class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
{
template <typename S>
using if_qualification_conversion = std::enable_if_t<
is_qualification_conversion_v<S, T>
, bool>;
protected:
using Base = QSpanCommon<T>;
// data members:
T *m_data;
qsizetype m_size;
// constructors (need to be public d/t the way ctor inheriting works):
public:
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
template <typename It, typename Base::template if_compatible_iterator<It> = true>
Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
: m_data{q20::to_address(first)}, m_size{count} {}
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
Q_IMPLICIT constexpr QSpanBase(It first, End last)
: QSpanBase(first, last - first) {}
template <size_t N>
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
: QSpanBase(arr, N) {}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename Range, typename Base::template if_compatible_range<Range> = true>
Q_IMPLICIT constexpr QSpanBase(Range &&r)
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
{}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
: QSpanBase(other.data(), other.size())
{}
#if __cpp_lib_span
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
: QSpanBase(other.data(), other.size())
{}
#endif // __cpp_lib_span
}; // class QSpanBase (dynamic extent)
} // namespace QSpanPrivate
template <typename T, std::size_t E>
class QSpan
#ifndef Q_QDOC
: private QSpanPrivate::QSpanBase<T, E>
#endif
{
using Base = QSpanPrivate::QSpanBase<T, E>;
Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
[[maybe_unused]] qsizetype n = 1) const
{
Q_ASSERT(pos >= 0);
Q_ASSERT(pos <= size());
Q_ASSERT(n >= 0);
Q_ASSERT(n <= size() - pos);
}
template <std::size_t N>
static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
public:
// constants and types
using element_type = T;
using value_type = std::remove_cv_t<element_type>;
using size_type = qsizetype; // difference to std::span
using difference_type = qptrdiff; // difference to std::span
using pointer = element_type*;
using const_pointer = const element_type*;
using reference = element_type&;
using const_reference = const element_type&;
using iterator = pointer; // implementation-defined choice
using const_iterator = const_pointer; // implementation-defined choice
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::size_t extent = E;
// [span.cons], constructors, copy, and assignment
using Base::Base;
#ifdef Q_QDOC
template <typename It> using if_compatible_iterator = bool;
template <typename S> using if_qualification_conversion = bool;
template <typename Range> using if_compatible_range = bool;
template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, qsizetype count);
template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, It last);
template <size_t N> constexpr QSpan(q20::type_identity_t<T> (&arr)[N]) noexcept;
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::array<S, N> &arr) noexcept;
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(const std::array<S, N> &arr) noexcept;
template <typename Range, if_compatible_range<Range> = true> constexpr QSpan(Range &&r);
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(QSpan<S, N> other) noexcept;
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<S, N> other) noexcept;
#endif // Q_QDOC
// [span.obs]
[[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
[[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
// [span.elem]
[[nodiscard]] constexpr reference operator[](size_type idx) const
{ verify(idx); return data()[idx]; }
[[nodiscard]] constexpr reference front() const { verify(); return *data(); }
[[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
[[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
// [span.iterators]
[[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
[[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
[[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
[[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
// [span.sub]
template <std::size_t Count>
[[nodiscard]] constexpr QSpan<T, Count> first() const
noexcept(subspan_always_succeeds_v<Count>)
{
static_assert(Count <= E,
"Count cannot be larger than the span's extent.");
verify(0, Count);
return QSpan<T, Count>{data(), Count};
}
template <std::size_t Count>
[[nodiscard]] constexpr QSpan<T, Count> last() const
noexcept(subspan_always_succeeds_v<Count>)
{
static_assert(Count <= E,
"Count cannot be larger than the span's extent.");
verify(0, Count);
return QSpan<T, Count>{data() + (size() - Count), Count};
}
template <std::size_t Offset>
[[nodiscard]] constexpr auto subspan() const
noexcept(subspan_always_succeeds_v<Offset>)
{
static_assert(Offset <= E,
"Offset cannot be larger than the span's extent.");
verify(Offset, 0);
if constexpr (E == q20::dynamic_extent)
return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
else
return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
}
template <std::size_t Offset, std::size_t Count>
[[nodiscard]] constexpr auto subspan() const
noexcept(subspan_always_succeeds_v<Offset + Count>)
{ return subspan<Offset>().template first<Count>(); }
[[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
[[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
// Qt-compatibility API:
[[nodiscard]] bool isEmpty() const noexcept { return empty(); }
// nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
}; // class QSpan
// [span.deduct]
template <class It, class EndOrSize>
QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
template <class T, std::size_t N>
QSpan(T (&)[N]) -> QSpan<T, N>;
template <class T, std::size_t N>
QSpan(std::array<T, N> &) -> QSpan<T, N>;
template <class T, std::size_t N>
QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
template <class R>
QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
QT_END_NAMESPACE
#endif // QSPAN_H

View File

@ -0,0 +1,639 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
\class QSpan
\inmodule QtCore
\since 6.7
\brief A non-owning container over contiguous data.
\ingroup tools
\reentrant
A QSpan references a contiguous portion of another contiguous container.
It acts as an interface type for all kinds of contiguous containers,
without the need to construct an owning container such as QList or
std::vector first.
The data referenced by a QSpan may be represented as an array (or
array-compatible data-structure such as QList, std::vector,
QVarLengthArray, etc.). QSpan itself merely stores a pointer to the data,
so users must ensure that QSpan objects do not outlive the data they
reference.
Unlike views such as QStringView, QLatin1StringView and QUtf8StringView,
referenced data can be modified through a QSpan object. To prevent this,
construct a QSpan over a \c{const T}:
\code
int numbers[] = {0, 1, 2};
QSpan<int> span = numbers;
span[0] = 42;
// numbers == {42, 1, 2};
QSpan<const int> cspan = numbers;
cspan[0] = 0; // ERROR: cspan[0] is read-only
\endcode
A QSpan can be \e{fixed-size} or \e{variable-sized}.
A variable-sized span is formed by omitting the second template argument
(or setting it to \c{std::dynamic_extent}, which is, however, only
available in C++20 builds), as seen in the example above.
A fixed-size span is formed by passing a number as the second template
argument:
\code
int numbers[] = {0, 1, 2};
QSpan<int, 3> span = numbers;
QSpan<const int, 3> = numbers; // also OK
\endcode
As the name suggests, a fixed-size span's size() is fixed at compile-time
whereas the size() of a variable-sized span is determined only at run-time.
A fixed-size span is not default-constructible (unless its \l extent is zero
(0)). A variable-sized span \e{is} default-constructible and will have
\c{data() == nullptr} and \c{size() == 0}.
A fixed-size span can be implicitly converted into a variable-sized one.
The opposite direction (variable-length into fixed-length) has the
precondition that both span's sizes must match.
Unlike with owning containers, \c{const} is \e{shallow} in QSpan: you can
still modify the data through a const QSpan (but not through a
\c{QSpan<const T>}), and begin() and end() are not overloaded on
\c{const}/non-\c{const}. There are cbegin() and cend(), though, that return
const_iterators which prevent modification of the data even though \c{T} is
not const:
\code
int numbers[] = {0, 1, 2};
const QSpan<int> span = numbers;
span.front() = 42; // OK, numbers[0] == 42 now
*span.begin() = 31; // OK, numbers[0] == 31 now
*span.cbegin() = -1; // ERROR: cannot assign through a const_iterator
\endcode
QSpan should be passed by value, not by reference-to-const:
\code
void consume(QSpan<const int> data); // OK
void consume(const QSpan<const int> &data); // works, but is non-idiomatic and less efficient
\endcode
\c{QSpan<T,N>} is a \e{Literal Type}, regardless of whether \c{T} is a
Literal Type or not.
\section2 QSpan vs. std::span
\target span-STL
QSpan is closely modelled after
\l{https://en.cppreference.com/w/cpp/container/span}{std::span}, but has a
few differences which we'll discuss here. Since they both implicitly
convert into each other, you're free to choose whichever one you like best
in your own code.
\list
\li QSpan is using the signed qsizetype as \c{size_type}
whereas \c{std::span} uses \c{size_t}.
\li All QSpan constructors are implicit;
many \c{std::span} ones are \c{explicit}.
\li QSpan can be constructed from rvalue owning containers, \c{std::span} can not.
\endlist
The last two are required for source-compatibility when functions that took
owning containers are converted to take QSpan instead, which is a
vitally-important use-case in Qt. The use of qsizetype is for consistency
with the rest of Qt containers. QSpan template arguments still use size_t
to avoid introducing unnecessary error conditions (negative sizes).
\section2 Compatible Iterators
\target span-compatible-iterators
QSpan can be constructed from an iterator and size or from an
iterator pair, provided the iterators are \e{compatible} ones.
Eventually, this should mean C++20 \c{std::contiguous_iterator} and
\c{std::sentinel_for}, but while Qt still supports C++17, only raw pointers
are considered contiguous iterators.
\section2 Compatible Ranges
\target span-compatible-ranges
QSpan can also be constructed from a \e{compatible} range. A range is
compatible if it has \l{span-compatible-iterators}{compatible iterators}.
\sa QList, QStringView, QLatin1StringView, QUtf8StringView
*/
//
// Nested types and constants
//
/*!
\typedef QSpan::element_type
An alias for \c{T}. Includes the \c{const}, if any.
This alias is provided for compatbility with the STL.
\sa value_type, pointer
*/
/*!
\typedef QSpan::value_type
An alias for \c{T}. Excludes the \c{const}, if any.
This alias is provided for compatbility with the STL.
\sa element_type
*/
/*!
\typedef QSpan::size_type
An alias for qsizetype. This \l{span-STL}{differs from \c{std::span}}.
This alias is provided for compatbility with the STL.
*/
/*!
\typedef QSpan::difference_type
An alias for qptrdiff. This \l{span-STL}{differs from \c{std::span}}.
This alias is provided for compatbility with the STL.
*/
/*!
\typedef QSpan::pointer
An alias for \c{T*} and \c{element_type*}, respectively. Includes the \c{const}, if any.
This alias is provided for compatbility with the STL.
\sa element_type, const_pointer, reference, iterator
*/
/*!
\typedef QSpan::const_pointer
An alias for \c{const T*} and \c{const element_type*}, respectively.
This alias is provided for compatbility with the STL.
\sa element_type, pointer, const_reference, const_iterator
*/
/*!
\typedef QSpan::reference
An alias for \c{T&} and \c{element_type&}, respectively. Includes the \c{const}, if any.
This alias is provided for compatbility with the STL.
\sa element_type, const_reference, pointer
*/
/*!
\typedef QSpan::const_reference
An alias for \c{const T&} and \c{const element_type&}, respectively.
This alias is provided for compatbility with the STL.
\sa element_type, reference, const_pointer
*/
/*!
\typedef QSpan::iterator
An alias for \c{T*} and \c{pointer}, respectively. Includes the \c{const}, if any.
\sa pointer, const_iterator, reverse_iterator
*/
/*!
\typedef QSpan::const_iterator
An alias for \c{const T*} and \c{const_pointer}, respectively.
\sa const_pointer, iterator, const_reverse_iterator
*/
/*!
\typedef QSpan::reverse_iterator
An alias for \c{std::reverse_iterator<iterator>}. Includes the \c{const}, if any.
\sa iterator, const_reverse_iterator
*/
/*!
\typedef QSpan::const_reverse_iterator
An alias for \c{std::reverse_iterator<const_iterator>}.
\sa const_iterator, reverse_iterator
*/
/*!
\variable QSpan::extent
The second template argument of \c{QSpan<T, E>}, that is, \c{E}. This is
\c{std::dynamic_extent} for variable-sized spans.
\note While all other sizes and indexes in QSpan use qsizetype, this
variable, like \c{E}, is actually of type \c{size_t}, for compatibility with
\c{std::span} and \c{std::dynamic_extent}.
\sa size()
*/
//
// Constructors and SMFs
//
/*!
\fn template <typename T, size_t E> QSpan<T,E>::QSpan()
Default constructor.
This constructor is only present if \c{E} is either zero (0) or
\c{std::dynamic_extent}. In other words: only fixed-zero-sized or variable-sized spans
are default-constructible.
\sa extent
*/
/*!
\fn template <typename T, size_t E> QSpan<T,E>::QSpan(const QSpan &other)
\fn template <typename T, size_t E> QSpan<T,E>::QSpan(QSpan &&other)
\fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(const QSpan &other)
\fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(QSpan &&other)
\fn template <typename T, size_t E> QSpan<T,E>::~QSpan()
These Special Member Functions are implicitly-defined.
\note Moves are equivalent to copies. Only data() and size() are copied
from span to span, not the referenced data.
*/
/*!
\fn template <typename T, size_t E> template <typename It, if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, qsizetype count)
Constructs a QSpan referencing the data starting at \a first and having length
\a count.
\c{[first, count)} must be a valid range.
\note This constructor participates in overload resolution only if \c{It}
is \l{span-compatible-iterators}{a compatible iterator}.
*/
/*!
\fn template <typename T, size_t E> template <typename It, if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, It last)
Constructs a QSpan referencing the data starting at \a first and having length
(\a last - \a first).
\c{[first, last)} must be a valid range.
\note This constructor participates in overload resolution only if \c{It}
is \l{span-compatible-iterators}{a compatible iterator}.
*/
/*!
\fn template <typename T, size_t E> template <size_t N> QSpan<T,E>::QSpan(q20::type_identity_t<T> (&arr)[N]);
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::array<S, N> &arr);
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(const std::array<S, N> &arr);
Constructs a QSpan referencing the data in the supplied array \a arr.
\note This constructor participates in overload resolution only if
\list
\li either \c{N} or \l{extent} are \c{std::dynamic_extent} or otherwise \l{extent} \c{==} \c{N}
\li and either \c{S} or \c{const S} are the same as \c{T}.
\endlist
\note \c{q20::type_identity_t} is a C++17 backport of C++20's
\l{https://en.cppreference.com/w/cpp/types/type_identity}{\c{std::type_identity_t}}.
*/
/*!
\fn template <typename T, size_t E> template <typename Range, if_compatible_range<Range> = true> QSpan<T,E>::QSpan(Range &&r)
Constructs a QSpan referencing the data in the supplied range \a r.
\note This constructor participates in overload resolution only if \c{Range}
is \l{span-compatible-ranges}{a compatible range}.
*/
/*!
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(QSpan<S, N> other);
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::span<S, N> other);
Constructs a QSpan referencing the data in the supplied span \a other.
\note This constructor participates in overload resolution only if
\list
\li either \c{N} or \l{extent} are \c{std::dynamic_extent} or \l{extent} \c{==} \c{N}
\li and either \c{S} or \c{const S} are the same as \c{T}.
\endlist
*/
//
// Member functions: sizes
//
/*!
\fn template <typename T, size_t E> QSpan<T, E>::size() const
Returns the size of the span, that is, the number of elements it references.
\sa size_bytes(), empty(), isEmpty()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::size_bytes() const
Returns the size of the span in bytes, that is, the number of elements
multiplied by \c{sizeof(T)}.
\sa size(), empty(), isEmpty()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::empty() const
\fn template <typename T, size_t E> QSpan<T, E>::isEmpty() const
Returns whether the span is empty, that is, whether \c{size() == 0}.
These functions do the same thing: empty() is provided for STL
compatibility and isEmpty() is provided for Qt compatibility.
\sa size(), size_bytes()
*/
//
// element access
//
/*!
\fn template <typename T, size_t E> QSpan<T, E>::operator[](size_type idx) const
Returns a reference to the element at index \a idx in the span.
The index must be in range, that is, \a idx >= 0 and \a idx < size(),
otherwise the behavior is undefined.
\sa front(), back(), size(), empty()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::front() const
Returns a reference to the first element in the span.
The span must not be empty, otherwise the behavior is undefined.
\sa operator[](), back(), size(), empty()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::back() const
Returns a reference to the last element in the span.
The span must not be empty, otherwise the behavior is undefined.
\sa operator[](), front(), size(), empty()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::data() const
Returns a pointer to the beginning of the span.
The same as calling begin().
\sa begin(), front()
*/
//
// iterators
//
/*!
\fn template <typename T, size_t E> QSpan<T, E>::begin() const
Returns an interator pointing at the beginning of the span.
Because QSpan iterators are just pointers, this is the same as calling
data().
\sa end(), cbegin(), rbegin(), crbegin(), data()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::end() const
Returns an iterator pointing to one past the end of the span.
Because QSpan iterators are just pointers, this it the same as calling
\c{data() + size()}.
\sa begin(), cend(), rend(), crend(), data(), size()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::cbegin() const
Returns a const_iterator pointing to the beginning of the span.
This will return a read-only iterator even if \c{T} is not \c{const}:
\code
QSpan<int> span = ~~~;
*span.begin() = 42; // OK
*span.cbegin() = 42; // ERROR: cannot assign through a const_iterator
\endcode
\sa cend(), begin(), crbegin(), rbegin(), data()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::cend() const
Returns a const_iterator pointing to one past the end of the span.
\sa cbegin(), end(), crend(), rend(), data(), size()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::rbegin() const
Returns a reverse_iterator pointing to the beginning of the reversed span.
\sa rend(), crbegin(), begin(), cbegin()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::rend() const
Returns a reverse_iterator pointing to one past the end of the reversed span.
\sa rbegin(), crend(), end(), cend()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::crbegin() const
Returns a const_reverse_iterator pointing to the beginning of the reversed span.
\sa crend(), rbegin(), cbegin(), begin()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::crend() const
Returns a const_reverse_iterator pointing to one past the end of the reversed span.
\sa crbegin(), rend(), cend(), end()
*/
//
// compile-time subspans:
//
/*!
\fn template <typename T, size_t E> template <size_t Count> QSpan<T, E>::first() const
\keyword first-t
Returns a fixed-sized span of size \c{Count} referencing the first \c{Count} elements of \c{*this}.
The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
size() >= \c{Count}), otherwise the behavior is undefined.
\sa first(QSpan::size_type), last(), subspan()
*/
/*!
\fn template <typename T, size_t E> template <size_t Count> QSpan<T, E>::last() const
\keyword last-t
Returns a fixed-sized span of size \c{Count} referencing the last \c{Count} elements of \c{*this}.
The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
size() >= \c{Count}), otherwise the behavior is undefined.
\sa last(QSpan::size_type), first(), subspan()
*/
/*!
\fn template <typename T, size_t E> template <size_t Offset> QSpan<T, E>::subspan() const
\keyword subspan-t1
Returns a span of size \c{E - Offset} referencing the remainder of this span
after dropping the first \c{Offset} elements.
If \c{*this} is a variable-sized span, the return type is a variable-sized
span, otherwise it is a fixed-sized span.
This span must hold at least \c{Offset} elements (\c{E} >= \c{Offset} \e{and}
size() >= \c{Offset}), otherwise the behavior is undefined.
\sa subspan(QSpan::size_type), subspan(), first(), last()
*/
#if 0 // needs fix for QTBUG-118080 integrated into qt5.git
/*!
\fn template <typename T, size_t E> template <size_t Offset, size_t Count> QSpan<T, E>::subspan() const
\keyword subspan-t2
Returns a span of size \c{Count} referencing the \c{Count} elements of this
span starting at \c{Offset}.
If \c{*this} is a variable-sized span, the return type is a variable-sized
span, otherwise it is a fixed-sized span.
This span must hold at least \c{Offset + Count} elements (\c{E} >=
\c{Offset + Count} \e{and} size() >= \c{Offset + Count}), otherwise the
behavior is undefined.
\sa subspan(QSpan::size_type, QSpan::size_type), subspan(), first(), last()
*/
#endif
//
// runtime subspans:
//
/*!
\fn template <typename T, size_t E> QSpan<T, E>::first(qsizetype n) const
\keyword first-n
Returns a variable-sized span of size \a n referencing the first \a n elements of \c{*this}.
\a n must be non-negative.
The span must hold at least \a n elements (\c{E} >= \a n \e{and} size() >=
\a n), otherwise the behavior is undefined.
\sa {first-t}{first<N>()}, last(QSpan::size_type), subspan(QSpan::size_type),
subspan(QSpan::size_type, QSpan::size_type)
\sa sliced()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::last(qsizetype n) const
\keyword last-n
Returns a variable-sized span of size \a n referencing the last \a n elements of \c{*this}.
\a n must be non-negative.
The span must hold at least \a n elements (\c{E} >= \a n \e{and}
size() >= \a n), otherwise the behavior is undefined.
\sa last(), first(QSpan::size_type), subspan(QSpan::size_type),
subspan(QSpan::size_type, QSpan::size_type), sliced()
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::subspan(qsizetype pos) const
\fn template <typename T, size_t E> QSpan<T, E>::sliced(qsizetype pos) const
\keyword subspan-n1
Returns a variable-sized span of size \c{size() - pos} referencing the
remainder of this span after dropping the first \a pos elements.
\a pos must be non-negative.
This span must hold at least \a pos elements (\c{E} >= \a pos \e{and}
size() >= \a pos), otherwise the behavior is undefined.
These functions do the same thing: subspan() is provided for STL
compatibility and sliced() is provided for Qt compatibility.
\sa subspan(), first(QSpan::size_type), last(QSpan::size_type)
*/
/*!
\fn template <typename T, size_t E> QSpan<T, E>::subspan(qsizetype pos, qsizetype n) const
\fn template <typename T, size_t E> QSpan<T, E>::sliced(qsizetype pos, qsizetype n) const
\keyword subspan-n2
Returns a variable-sized span of size \a n referencing the \a n elements of
this span starting at \a pos.
Both \a pos and \a n must be non-negative.
This span must hold at least \c{pos + n} elements (\c{E} >=
\c{pos + n} \e{and} size() >= \c{pos + n}), otherwise the
behavior is undefined.
These functions do the same thing: subspan() is provided for STL
compatibility and sliced() is provided for Qt compatibility.
\sa subspan(), first(QSpan::size_type), last(QSpan::size_type)
*/

View File

@ -4,6 +4,8 @@
#ifndef QSPAN_P_H
#define QSPAN_P_H
#include <QtCore/qspan.h>
//
// W A R N I N G
// -------------
@ -15,409 +17,8 @@
// We mean it.
//
#include <QtCore/qcompilerdetection.h>
#include <QtCore/qtypes.h>
#include <array>
#include <cstddef>
#include <cassert>
#include <QtCore/q20iterator.h>
#include <QtCore/q20memory.h>
#ifdef __cpp_lib_span
#include <span>
#endif
#include <QtCore/q20type_traits.h>
QT_BEGIN_NAMESPACE
// like std::dynamic_extent
namespace q20 {
inline constexpr auto dynamic_extent = std::size_t(-1);
} // namespace q20
template <typename T, std::size_t E = q20::dynamic_extent> class QSpan;
QT_BEGIN_INCLUDE_NAMESPACE
#ifdef __cpp_lib_span
#ifdef __cpp_lib_concepts
namespace std::ranges {
// Officially, these are defined in <ranges>, but that is a heavy-hitter header.
// OTOH, <span> must specialize these variable templates, too, so we assume that
// <span> includes some meaningful subset of <ranges> and just go ahead and use them:
template <typename T, std::size_t E>
constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
template <typename T, std::size_t E>
constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
} // namespace std::ranges
#endif // __cpp_lib_concepts
#endif // __cpp_lib_span
QT_END_INCLUDE_NAMESPACE
namespace QSpanPrivate {
template <typename T, std::size_t E> class QSpanBase;
template <typename T>
struct is_qspan_helper : std::false_type {};
template <typename T, std::size_t E>
struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
template <typename T, std::size_t E>
struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
template <typename T>
using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
template <typename T>
struct is_std_span_helper : std::false_type {};
#ifdef __cpp_lib_span
template <typename T, std::size_t E>
struct is_std_span_helper<std::span<T, E>> : std::true_type {};
#endif // __cpp_lib_span
template <typename T>
using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
template <typename T>
struct is_std_array_helper : std::false_type {};
template <typename T, std::size_t N>
struct is_std_array_helper<std::array<T, N>> : std::true_type {};
template <typename T>
using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
template <typename From, typename To>
using is_qualification_conversion =
std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1
template <typename From, typename To>
constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
// not doing the extra work C++20 requires
template <typename Range>
decltype(auto) adl_begin(Range &&r) { using std::begin; return begin(r); }
template <typename Range>
decltype(auto) adl_data(Range &&r) { using std::data; return data(r); }
template <typename Range>
decltype(auto) adl_size(Range &&r) { using std::size; return size(r); }
// Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin)
// This one uses adl_begin() instead.
template <typename Range>
using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
template <typename Range>
using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
template <typename T>
class QSpanCommon {
protected:
template <typename Iterator>
using is_compatible_iterator = std::conjunction<
std::is_base_of<
std::random_access_iterator_tag,
typename std::iterator_traits<Iterator>::iterator_category
>,
is_qualification_conversion<
std::remove_reference_t<q20::iter_reference_t<Iterator>>,
T
>
>;
template <typename Iterator, typename End>
using is_compatible_iterator_and_sentinel = std::conjunction<
is_compatible_iterator<Iterator>,
std::negation<std::is_convertible<End, std::size_t>>
>;
template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t:
struct is_compatible_range_helper : std::false_type {};
template <typename Range>
struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
: is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
template <typename Range>
using is_compatible_range = std::conjunction<
// ### this needs more work, esp. extension to C++20 contiguous iterators
std::negation<is_qspan<Range>>,
std::negation<is_std_span<Range>>,
std::negation<is_std_array<Range>>,
std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
is_compatible_range_helper<Range>
>;
// constraints
template <typename Iterator>
using if_compatible_iterator = std::enable_if_t<
is_compatible_iterator<Iterator>::value
, bool>;
template <typename Iterator, typename End>
using if_compatible_iterator_and_sentinel = std::enable_if_t<
is_compatible_iterator_and_sentinel<Iterator, End>::value
, bool>;
template <typename Range>
using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
}; // class QSpanCommon
template <typename T, std::size_t E>
class QSpanBase : protected QSpanCommon<T>
{
static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
"QSpan only supports extents that fit into the signed size type (qsizetype).");
struct Enabled_t { explicit Enabled_t() = default; };
static inline constexpr Enabled_t Enable{};
template <typename S, std::size_t N>
using if_compatible_array = std::enable_if_t<
N == E && is_qualification_conversion_v<S, T>
, bool>;
template <typename S>
using if_qualification_conversion = std::enable_if_t<
is_qualification_conversion_v<S, T>
, bool>;
protected:
using Base = QSpanCommon<T>;
// data members:
T *m_data;
static constexpr qsizetype m_size = qsizetype(E);
// types and constants:
// (in QSpan only)
// constructors (need to be public d/t the way ctor inheriting works):
public:
template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
template <typename It, typename Base::template if_compatible_iterator<It> = true>
explicit constexpr QSpanBase(It first, qsizetype count)
: m_data{q20::to_address(first)}
{
Q_ASSERT(count == m_size);
}
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
explicit constexpr QSpanBase(It first, End last)
: QSpanBase(first, last - first) {}
template <size_t N, std::enable_if_t<N == E, bool> = true>
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
: QSpanBase(arr, N) {}
template <typename S, size_t N, if_compatible_array<S, N> = true>
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename S, size_t N, if_compatible_array<S, N> = true>
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename Range, typename Base::template if_compatible_range<Range> = true>
Q_IMPLICIT constexpr QSpanBase(Range &&r)
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
: QSpanBase(other.data(), other.size())
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
: QSpanBase(other.data(), other.size())
{}
#ifdef __cpp_lib_span
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
: QSpanBase(other.data(), other.size())
{}
template <typename S, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
: QSpanBase(other.data(), other.size())
{}
#endif // __cpp_lib_span
}; // class QSpanBase (fixed extent)
template <typename T>
class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
{
template <typename S>
using if_qualification_conversion = std::enable_if_t<
is_qualification_conversion_v<S, T>
, bool>;
protected:
using Base = QSpanCommon<T>;
// data members:
T *m_data;
qsizetype m_size;
// constructors (need to be public d/t the way ctor inheriting works):
public:
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
template <typename It, typename Base::template if_compatible_iterator<It> = true>
Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
: m_data{q20::to_address(first)}, m_size{count} {}
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
Q_IMPLICIT constexpr QSpanBase(It first, End last)
: QSpanBase(first, last - first) {}
template <size_t N>
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
: QSpanBase(arr, N) {}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
: QSpanBase(arr.data(), N) {}
template <typename Range, typename Base::template if_compatible_range<Range> = true>
Q_IMPLICIT constexpr QSpanBase(Range &&r)
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
{}
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
: QSpanBase(other.data(), other.size())
{}
#if __cpp_lib_span
template <typename S, size_t N, if_qualification_conversion<S> = true>
Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
: QSpanBase(other.data(), other.size())
{}
#endif // __cpp_lib_span
}; // class QSpanBase (dynamic extent)
} // namespace QSpanPrivate
template <typename T, std::size_t E>
class QSpan
#ifndef Q_QDOC
: private QSpanPrivate::QSpanBase<T, E>
#endif
{
using Base = QSpanPrivate::QSpanBase<T, E>;
Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
[[maybe_unused]] qsizetype n = 1) const
{
Q_ASSERT(pos >= 0);
Q_ASSERT(pos <= size());
Q_ASSERT(n >= 0);
Q_ASSERT(n <= size() - pos);
}
template <std::size_t N>
static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
public:
// constants and types
using element_type = T;
using value_type = std::remove_cv_t<element_type>;
using size_type = qsizetype; // difference to std::span
using difference_type = qptrdiff; // difference to std::span
using pointer = element_type*;
using const_pointer = const element_type*;
using reference = element_type&;
using const_reference = const element_type&;
using iterator = pointer; // implementation-defined choice
using const_iterator = const_pointer; // implementation-defined choice
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::size_t extent = E;
// [span.cons], constructors, copy, and assignment
using Base::Base;
// [span.obs]
[[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
[[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
// [span.elem]
[[nodiscard]] constexpr reference operator[](size_type idx) const
{ verify(idx); return data()[idx]; }
[[nodiscard]] constexpr reference front() const { verify(); return *data(); }
[[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
[[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
// [span.iterators]
[[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
[[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
[[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
[[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
// [span.sub]
template <std::size_t Count>
[[nodiscard]] constexpr QSpan<T, Count> first() const
noexcept(subspan_always_succeeds_v<Count>)
{
static_assert(Count <= E,
"Count cannot be larger than the span's extent.");
verify(0, Count);
return QSpan<T, Count>{data(), Count};
}
template <std::size_t Count>
[[nodiscard]] constexpr QSpan<T, Count> last() const
noexcept(subspan_always_succeeds_v<Count>)
{
static_assert(Count <= E,
"Count cannot be larger than the span's extent.");
verify(0, Count);
return QSpan<T, Count>{data() + (size() - Count), Count};
}
template <std::size_t Offset>
[[nodiscard]] constexpr auto subspan() const
noexcept(subspan_always_succeeds_v<Offset>)
{
static_assert(Offset <= E,
"Offset cannot be larger than the span's extent.");
verify(Offset, 0);
if constexpr (E == q20::dynamic_extent)
return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
else
return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
}
template <std::size_t Offset, std::size_t Count>
[[nodiscard]] constexpr auto subspan() const
noexcept(subspan_always_succeeds_v<Offset + Count>)
{ return subspan<Offset>().template first<Count>(); }
[[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
[[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
// Qt-compatibility API:
[[nodiscard]] bool isEmpty() const noexcept { return empty(); }
// nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
}; // class QSpan
// [span.deduct]
template <class It, class EndOrSize>
QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
template <class T, std::size_t N>
QSpan(T (&)[N]) -> QSpan<T, N>;
template <class T, std::size_t N>
QSpan(std::array<T, N> &) -> QSpan<T, N>;
template <class T, std::size_t N>
QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
template <class R>
QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
QT_END_NAMESPACE
#endif // QSPAN_P_H

View File

@ -26,6 +26,7 @@
#include "QtCore/qlist.h"
#include "QtCore/qnamespace.h"
#include "QtCore/qset.h"
#include <QtCore/qspan.h>
#include "QtCore/qstring.h"
#include "QtCore/qvarlengtharray.h"
@ -33,7 +34,6 @@
#include "private/qfont_p.h"
#include "private/qtextformat_p.h"
#include "private/qunicodetools_p.h"
#include "private/qspan_p.h"
#ifndef QT_BUILD_COMPAT_LIB
#include "private/qtextdocument_p.h"
#endif

View File

@ -16,11 +16,11 @@
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/qspan.h>
#include <QtCore/QStandardPaths>
#include <QtCore/QTemporaryDir>
#include <QtCore/QTextStream>
#include <QtConcurrent/QtConcurrentRun>
#include <private/qspan_p.h>
#include <QtCore/private/qduplicatetracker_p.h>
#include <QTest>

View File

@ -4,6 +4,4 @@
qt_internal_add_test(tst_qspan
SOURCES
tst_qspan.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -1,7 +1,7 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qspan_p.h>
#include <QSpan>
#include <QList>
#include <QTest>