QSpan: add construction from initializer_list
P2447 has been merged in C++26, backport the same functionality.
This makes QSpan<const T> a proper replacement for a const QList<T>&
parameter, because now both can be built via a braced-init-list.
// void f(const QList<int> &l); // old
void f(QSpan<const int>); // new
f({1, 2, 3}); // now OK
This is, technically speaking, SiC: in the presence of both `f`
overloads, the code above would have called the QList one. Now instead
the call is ambiguous.
We've been there already -- this is QString and QStringView all over
again, and the solution is the same: get rid of the owning container
overload. I'd rather have this construction *sooner* rather than *later*
in order to minimize the fallout.
And just like QString vs QStringView, there's nothing really doable to
prevent instant-dangling situations:
QStringView v = getString(); // dangles
QSpan<const int> s = {1, 2, 3}; // ditto
except for using QSpan (QStringView) as a *parameter type only*.
Note that QSpan with dynamic extent was already convertible from
std::initializer_list through its ranged constructor. However this fact
alone doesn't unlock the above syntax. QSpan with a static extent was
also convertible for the same reason. (This is non-standard:
std::span's range constructor for static extents is explicit, but QSpan
doesn't follow that design choice and makes the constructors implicit
instead.)
Found in API-review.
Pick-to: 6.7
Change-Id: I160ab5b292b0c2568cd9a7ad1b4430085f475c29
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
bb10
parent
a83248dd9f
commit
7f7b5ff3a1
|
|
@ -11,6 +11,7 @@
|
|||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <QtCore/q20iterator.h>
|
||||
#include <QtCore/q20memory.h>
|
||||
#ifdef __cpp_lib_span
|
||||
|
|
@ -224,6 +225,11 @@ public:
|
|||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il)
|
||||
: QSpanBase(il.begin(), il.size())
|
||||
{}
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
|
||||
|
|
@ -286,6 +292,11 @@ public:
|
|||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) noexcept
|
||||
: QSpanBase(il.begin(), il.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
|
||||
|
|
@ -347,6 +358,7 @@ public:
|
|||
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;
|
||||
constexpr QSpan(std::initializer_list<value_type> il);
|
||||
#endif // Q_QDOC
|
||||
|
||||
// [span.obs]
|
||||
|
|
|
|||
|
|
@ -341,6 +341,18 @@
|
|||
\endlist
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::QSpan(std::initializer_list<value_type> il);
|
||||
|
||||
Constructs a QSpan referencing the data in the supplied initializer list \a il.
|
||||
|
||||
\note This constructor participates in overload resolution only if \c{T} is \c{const}-qualified.
|
||||
|
||||
\note This constructor is \c{noexcept} only if \c{E} is \c{std::dynamic_extent}.
|
||||
|
||||
\note If \c{E} is not \c{std::dynamic_extent} and the size of \a il is not \c{E}, the behavior is undefined.
|
||||
*/
|
||||
|
||||
//
|
||||
// Member functions: sizes
|
||||
//
|
||||
|
|
|
|||
|
|
@ -99,6 +99,15 @@ static_assert(!std::is_convertible_v<QSpan<const int, 0>, std::span<int, 0>>);
|
|||
// Spans don't convert from nonsense:
|
||||
static_assert(!std::is_constructible_v<QSpan<const int>, int&&>);
|
||||
|
||||
// Span is constructible from initializer_list
|
||||
static_assert( std::is_convertible_v<std::initializer_list<int>, QSpan<const int>>);
|
||||
static_assert(!std::is_convertible_v<std::initializer_list<int>, QSpan< int>>);
|
||||
static_assert(!std::is_constructible_v<QSpan<int>, std::initializer_list<int>>);
|
||||
|
||||
static_assert( std::is_convertible_v<std::initializer_list<int>, QSpan<const int, 4>>); // non-standard, but QSpan considers initializer_list a range
|
||||
static_assert( std::is_constructible_v<QSpan<const int, 4>, std::initializer_list<int>>);
|
||||
static_assert(!std::is_constructible_v<QSpan< int, 4>, std::initializer_list<int>>);
|
||||
|
||||
class tst_QSpan : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -114,6 +123,7 @@ private Q_SLOTS:
|
|||
void fromZeroSizeStdArray() const;
|
||||
void fromStdVector() const;
|
||||
void fromQList() const;
|
||||
void fromInitList() const;
|
||||
|
||||
private:
|
||||
template <typename T, std::size_t N>
|
||||
|
|
@ -322,9 +332,11 @@ void tst_QSpan::from_container_impl(C &&c) const
|
|||
{
|
||||
const auto c_size = qsizetype(QSpanPrivate::adl_size(c));
|
||||
const auto c_data = QSpanPrivate::adl_data(c);
|
||||
|
||||
using V = std::remove_reference_t<QSpanPrivate::range_reference_t<C>>;
|
||||
{
|
||||
QSpan si = c; // CTAD
|
||||
static_assert(std::is_same_v<decltype(si), QSpan<int, ExpectedExtent>>);
|
||||
static_assert(std::is_same_v<decltype(si), QSpan<V, ExpectedExtent>>);
|
||||
|
||||
QCOMPARE_EQ(si.size(), c_size);
|
||||
QCOMPARE_EQ(si.data(), c_data);
|
||||
|
|
@ -421,6 +433,17 @@ void tst_QSpan::fromQList() const
|
|||
from_variable_size_container_impl(li);
|
||||
}
|
||||
|
||||
void tst_QSpan::fromInitList() const
|
||||
{
|
||||
from_variable_size_container_impl(std::initializer_list<int>{42, 84, 168, 336});
|
||||
|
||||
auto l1 = [](QSpan<const int>){};
|
||||
l1({1, 2, 3});
|
||||
|
||||
auto l2 = [](QSpan<const int, 3>){};
|
||||
l2({4, 5, 6});
|
||||
}
|
||||
|
||||
#undef RETURN_IF_FAILED
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QSpan);
|
||||
|
|
|
|||
Loading…
Reference in New Issue