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. Change-Id: I160ab5b292b0c2568cd9a7ad1b4430085f475c29 Reviewed-by: Marc Mutz <marc.mutz@qt.io> (cherry picked from commit 7f7b5ff3a1b617a3a1add1b1b6ad0718f0dcf143) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
9ea3087d36
commit
c80f475055
@ -11,6 +11,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <initializer_list>
|
||||||
#include <QtCore/q20iterator.h>
|
#include <QtCore/q20iterator.h>
|
||||||
#include <QtCore/q20memory.h>
|
#include <QtCore/q20memory.h>
|
||||||
#ifdef __cpp_lib_span
|
#ifdef __cpp_lib_span
|
||||||
@ -224,6 +225,11 @@ public:
|
|||||||
: QSpanBase(other.data(), other.size())
|
: 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
|
#ifdef __cpp_lib_span
|
||||||
template <typename S, if_qualification_conversion<S> = true>
|
template <typename S, if_qualification_conversion<S> = true>
|
||||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
|
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
|
||||||
@ -286,6 +292,11 @@ public:
|
|||||||
: QSpanBase(other.data(), other.size())
|
: 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
|
#if __cpp_lib_span
|
||||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
|
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 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(QSpan<S, N> other) noexcept;
|
||||||
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<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
|
#endif // Q_QDOC
|
||||||
|
|
||||||
// [span.obs]
|
// [span.obs]
|
||||||
|
@ -341,6 +341,18 @@
|
|||||||
\endlist
|
\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
|
// 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:
|
// Spans don't convert from nonsense:
|
||||||
static_assert(!std::is_constructible_v<QSpan<const int>, int&&>);
|
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
|
class tst_QSpan : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -114,6 +123,7 @@ private Q_SLOTS:
|
|||||||
void fromZeroSizeStdArray() const;
|
void fromZeroSizeStdArray() const;
|
||||||
void fromStdVector() const;
|
void fromStdVector() const;
|
||||||
void fromQList() const;
|
void fromQList() const;
|
||||||
|
void fromInitList() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T, std::size_t N>
|
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_size = qsizetype(QSpanPrivate::adl_size(c));
|
||||||
const auto c_data = QSpanPrivate::adl_data(c);
|
const auto c_data = QSpanPrivate::adl_data(c);
|
||||||
|
|
||||||
|
using V = std::remove_reference_t<QSpanPrivate::range_reference_t<C>>;
|
||||||
{
|
{
|
||||||
QSpan si = c; // CTAD
|
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.size(), c_size);
|
||||||
QCOMPARE_EQ(si.data(), c_data);
|
QCOMPARE_EQ(si.data(), c_data);
|
||||||
@ -421,6 +433,17 @@ void tst_QSpan::fromQList() const
|
|||||||
from_variable_size_container_impl(li);
|
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
|
#undef RETURN_IF_FAILED
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(tst_QSpan);
|
QTEST_APPLESS_MAIN(tst_QSpan);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user