QVariant: add rvalue overload of fromStdVariant()

Extract Method fromStdVariantImpl() to share the otherwise
more-or-less identical implementation between the two overloads.

Don't use a constrained template version of fromStdVariantImpl() as
fromStdVariant(), because the constraint would have to be very complex
to continue allowing subclasses of std::variant to be passed.

As a drive-by, mark the valueless_by_exception() path Q_UNLIKELY.

[ChangeLog][QtCore][QVariant] Added overload of fromStdVariant()
taking rvalue std::variant<>s.

Fixes: QTBUG-114134
Change-Id: Ia1c7ae93ab421e6689dc9f2d8d0c2295b23cbbf6
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Marc Mutz 2023-05-31 16:34:01 +02:00
parent 79ae79d05c
commit 8983225d3c
3 changed files with 69 additions and 3 deletions

View File

@ -2721,6 +2721,12 @@ QT_WARNING_POP
\sa fromValue()
*/
/*!
\fn template<typename... Types> QVariant QVariant::fromStdVariant(std::variant<Types...> &&value)
\since 6.6
\overload
*/
/*!
\fn template<typename T> T qvariant_cast(const QVariant &value)
\relates QVariant

View File

@ -569,9 +569,13 @@ public:
template<typename... Types>
static inline QVariant fromStdVariant(const std::variant<Types...> &value)
{
if (value.valueless_by_exception())
return QVariant();
return std::visit([](const auto &arg) { return QVariant::fromValue(arg); }, value);
return fromStdVariantImpl(value);
}
template<typename... Types>
static QVariant fromStdVariant(std::variant<Types...> &&value)
{
return fromStdVariantImpl(std::move(value));
}
template<typename T>
@ -585,6 +589,17 @@ public:
static QPartialOrdering compare(const QVariant &lhs, const QVariant &rhs);
private:
template <typename StdVariant>
static QVariant fromStdVariantImpl(StdVariant &&v)
{
if (Q_UNLIKELY(v.valueless_by_exception()))
return QVariant();
auto visitor = [](auto &&arg) {
return QVariant::fromValue(q23::forward_like<StdVariant>(arg));
};
return std::visit(visitor, std::forward<StdVariant>(v));
}
friend inline bool operator==(const QVariant &a, const QVariant &b)
{ return a.equals(b); }
friend inline bool operator!=(const QVariant &a, const QVariant &b)

View File

@ -5392,6 +5392,16 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
void tst_QVariant::fromStdVariant()
{
#define CHECK_EQUAL(lhs, rhs, type) do { \
QCOMPARE(lhs.typeId(), rhs.typeId()); \
if (lhs.isNull()) { \
QVERIFY(rhs.isNull()); \
} else { \
QVERIFY(!rhs.isNull()); \
QCOMPARE(get< type >(lhs), get< type >(rhs)); \
} \
} while (false)
{
typedef std::variant<int, bool> intorbool_t;
intorbool_t stdvar = 5;
@ -5399,21 +5409,38 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Int);
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
{
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
CHECK_EQUAL(qv2, qvar, int);
}
stdvar = true;
qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Bool);
QCOMPARE(qvar.value<bool>(), std::get<bool>(stdvar));
{
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
CHECK_EQUAL(qv2, qvar, bool);
}
}
{
std::variant<std::monostate, int> stdvar;
QVariant qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isValid());
{
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
CHECK_EQUAL(qv2, qvar, int); // fake type, they're empty
}
stdvar = -4;
qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Int);
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
{
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
CHECK_EQUAL(qv2, qvar, int);
}
}
{
std::variant<int, bool, QChar> stdvar = QChar::fromLatin1(' ');
@ -5421,7 +5448,25 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::QChar);
QCOMPARE(qvar.value<QChar>(), std::get<QChar>(stdvar));
{
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
CHECK_EQUAL(qv2, qvar, QChar);
}
}
// rvalue fromStdVariant() actually moves:
{
const auto foo = u"foo"_s;
std::variant<QString, QByteArray> stdvar = foo;
QVariant qvar = QVariant::fromStdVariant(std::move(stdvar));
const auto ps = get_if<QString>(&stdvar);
QVERIFY(ps);
QVERIFY(ps->isNull()); // QString was moved from
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::QString);
QCOMPARE(get<QString>(qvar), foo);
}
#undef CHECK_EQUAL
}
void tst_QVariant::qt4UuidDataStream()