String-like containers: add implicit conversions towards std:: string views

In C++20 std::basic_string_view has gained a range constructor (like
QStringView always had), but that range constructor has been made
explicit. This means we can't just pass a QString(View) to a function
taking a u16string_view. The consensus seems to be that that types that
should implictly convert towards stdlib's string views should do that
via implicit conversion operators. This patch adds them for

* QByteArrayView => std::string_view
* QString(View) => std::u16string_view
* QUtf8StringView => std::string_view or std::u8string_view, depending
  on the storage_type

QLatin1StringView doesn't have a matching std:: view so I'm not enabling
its conversion.

QByteArray poses a challenge, in that it already defines a conversion
towards const char *. (One can disable that conversion with a macro.)
That conversion makes it impossible to support:

  QByteArray ba;
  std::string_view sv1(ba);  // 1
  std::string_view sv2 = ba; // 2

because:

* if only operator const char *() is defined, then (2) doesn't work
  (situation right now);

* if both conversions to const char * and string_view are defined, then
  (1) is ambiguous on certain compilers (MSVC, QCC). Interestingly
  enough, not on GCC/Clang, but only in C++17 and later modes.

I can't kill the conversion towards const char * (API break, and we use
it *everywhere* in Qt), hence, QByteArray does not get the implicit
conversion, at least not in this patch.

[ChangeLog][QtCore][QByteArrayView] Added an implicit conversion
operator towards std::string_view.

[ChangeLog][QtCore][QString] Added an implicit conversion operator
towards std::u16string_view.

[ChangeLog][QtCore][QStringView] Added an implicit conversion operator
towards std::u16string_view.

[ChangeLog][QtCore][QUtf8StringView] Added an implicit conversion
operator towards std::string_view (QUtf8StringView is using char
as its storage type in Qt 6). Note that QUtf8StringView is planned to
use char8_t in Qt 7, therefore it is expected that the conversion will
change towards std::u8string_view in Qt 7.

Change-Id: I6d3b64d211a386241ae157765cd1b03f531f909a
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Giuseppe D'Angelo 2023-05-21 13:38:09 +02:00
parent c07a3f10ef
commit 96d67da420
11 changed files with 130 additions and 0 deletions

View File

@ -301,6 +301,9 @@ public:
[[nodiscard]] constexpr char front() const { Q_ASSERT(!empty()); return m_data[0]; }
[[nodiscard]] constexpr char back() const { Q_ASSERT(!empty()); return m_data[m_size - 1]; }
[[nodiscard]] Q_IMPLICIT operator std::string_view() const noexcept
{ return std::string_view(m_data, size_t(m_size)); }
//
// Qt compatibility API:
//

View File

@ -1032,3 +1032,12 @@
\sa QByteArray::isNull(), QByteArrayView
*/
/*!
\fn QByteArrayView::operator std::string_view() const
\since 6.7
Converts this QByteArrayView object to a \c{std::string_view} object.
The returned view will have the same data pointer and length of
this view.
*/

View File

@ -2614,6 +2614,12 @@ QString::QString(QChar ch)
\internal
*/
/*! \fn QString::operator std::u16string_view() const
\since 6.7
Converts this QString object to a \c{std::u16string_view} object.
*/
static bool needsReallocate(const QString &str, qsizetype newSize)
{
const auto capacityAtEnd = str.capacity() - str.data_ptr().freeSpaceAtBegin();

View File

@ -883,6 +883,8 @@ public:
static inline QString fromStdU32String(const std::u32string &s);
inline std::u32string toStdU32String() const;
Q_IMPLICIT inline operator std::u16string_view() const noexcept;
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
static QString fromCFString(CFStringRef string);
CFStringRef toCFString() const Q_DECL_CF_RETURNS_RETAINED;
@ -1351,6 +1353,11 @@ inline std::u32string QString::toStdU32String() const
return u32str;
}
QString::operator std::u16string_view() const noexcept
{
return std::u16string_view(d.data(), size_t(d.size));
}
#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QString &);
Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QString &);

View File

@ -1411,4 +1411,13 @@ or the character \a ch
\sa QStringTokenizer, qTokenize()
*/
/*!
\fn QStringView::operator std::u16string_view() const
\since 6.7
Converts this QStringView object to a \c{std::u16string_view} object.
The returned view will have the same data pointer and length of
this view.
*/
QT_END_NAMESPACE

View File

@ -400,6 +400,9 @@ public:
[[nodiscard]] constexpr QChar front() const { return Q_ASSERT(!empty()), QChar(m_data[0]); }
[[nodiscard]] constexpr QChar back() const { return Q_ASSERT(!empty()), QChar(m_data[m_size - 1]); }
[[nodiscard]] Q_IMPLICIT operator std::u16string_view() const noexcept
{ return std::u16string_view(m_data, size_t(m_size)); }
//
// Qt compatibility API:
//

View File

@ -274,6 +274,9 @@ public:
[[nodiscard]] constexpr storage_type front() const { return Q_ASSERT(!empty()), m_data[0]; }
[[nodiscard]] constexpr storage_type back() const { return Q_ASSERT(!empty()), m_data[m_size - 1]; }
[[nodiscard]] Q_IMPLICIT operator std::basic_string_view<storage_type>() const noexcept
{ return std::basic_string_view<storage_type>(data(), size_t(size())); }
//
// Qt compatibility API:
//

View File

@ -695,3 +695,13 @@
\sa QByteArray::isNull(), QUtf8StringView
*/
/*! \fn QUtf8StringView::operator std::basic_string_view<storage_type>() const
\since 6.7
Converts this QUtf8StringView object to a
\c{std::basic_string_view} object. The returned view will have the
same data pointer and length of this view. The character type of
the returned view will be \c{storage_type}.
*/

View File

@ -171,6 +171,7 @@ private slots:
void comparison() const;
void compare() const;
void std_stringview_conversion();
private:
template <typename Data>
@ -632,5 +633,30 @@ void tst_QByteArrayView::compare() const
QVERIFY(alpha.compare(beta, Qt::CaseSensitive) > 0);
}
void tst_QByteArrayView::std_stringview_conversion()
{
static_assert(std::is_convertible_v<QByteArrayView, std::string_view>);
QByteArrayView bav;
std::string_view sv(bav);
QCOMPARE(sv, std::string_view());
bav = "";
sv = bav;
QCOMPARE(bav.size(), 0);
QCOMPARE(sv.size(), size_t(0));
QCOMPARE(sv, std::string_view());
bav = "Hello";
sv = bav;
QCOMPARE(sv, std::string_view("Hello"));
bav = QByteArrayView::fromArray("Hello\0world");
sv = bav;
QCOMPARE(bav.size(), 12);
QCOMPARE(sv.size(), size_t(12));
QCOMPARE(sv, std::string_view("Hello\0world", 12));
}
QTEST_APPLESS_MAIN(tst_QByteArrayView)
#include "tst_qbytearrayview.moc"

View File

@ -696,6 +696,8 @@ private slots:
void sliced();
void chopped();
void removeIf();
void std_stringview_conversion();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QString::DataOptions)
@ -8762,6 +8764,31 @@ void tst_QString::removeIf()
QCOMPARE(a.removeIf(removeA), u"BcbC");
}
void tst_QString::std_stringview_conversion()
{
static_assert(std::is_convertible_v<QString, std::u16string_view>);
QString s;
std::u16string_view sv(s);
QCOMPARE(sv, std::u16string_view());
s = u""_s;
sv = s;
QCOMPARE(s.size(), 0);
QCOMPARE(sv.size(), size_t(0));
QCOMPARE(sv, std::u16string_view());
s = u"Hello"_s;
sv = s;
QCOMPARE(sv, std::u16string_view(u"Hello"));
s = u"Hello\0world"_s;
sv = s;
QCOMPARE(s.size(), 11);
QCOMPARE(sv.size(), size_t(11));
QCOMPARE(sv, std::u16string_view(u"Hello\0world", 11));
}
// QString's collation order is only supported during the lifetime as QCoreApplication
QTEST_GUILESS_MAIN(tst_QString)

View File

@ -258,6 +258,8 @@ private Q_SLOTS:
void tokenize_data() const;
void tokenize() const;
void std_stringview_conversion();
private:
template <typename String>
void conversion_tests(String arg) const;
@ -879,5 +881,30 @@ void tst_QStringView::overloadResolution()
}
}
void tst_QStringView::std_stringview_conversion()
{
static_assert(std::is_convertible_v<QStringView, std::u16string_view>);
QStringView s;
std::u16string_view sv(s);
QCOMPARE(sv, std::u16string_view());
s = u"";
sv = s;
QCOMPARE(s.size(), 0);
QCOMPARE(sv.size(), size_t(0));
QCOMPARE(sv, std::u16string_view());
s = u"Hello";
sv = s;
QCOMPARE(sv, std::u16string_view(u"Hello"));
s = QStringView::fromArray(u"Hello\0world");
sv = s;
QCOMPARE(s.size(), 12);
QCOMPARE(sv.size(), size_t(12));
QCOMPARE(sv, std::u16string_view(u"Hello\0world\0", 12));
}
QTEST_APPLESS_MAIN(tst_QStringView)
#include "tst_qstringview.moc"