QVariant: Add support for in-place construction

This avoids constructing an object just to copy (later: move) it into a
QVariant.
ChangeLog will be in a follow-up change adding emplace support.

Task-number: QTBUG-112187
Change-Id: I444e580c7d8927d41b3d21d5a521e7c475119e4c
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Fabian Kosmale 2023-03-22 15:21:15 +01:00
parent 6e5258b48b
commit c81e8f8ff2
3 changed files with 99 additions and 0 deletions

View File

@ -523,6 +523,43 @@ QVariant::QVariant(const QVariant &p)
{
}
/*!
\fn template <typename Type, typename... Args, if_constructible<Type, Args...> = true> QVariant::QVariant(std::in_place_type_t<Type>, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, Args...>::value)
\since 6.6
Constructs a new variant containing a value of type \c Type. The contained
value is is initialized with the arguments
\c{std::forward<Args>(args)...}.
This overload only participates in overload resolution if \c Type can be
constructed from \a args.
This constructor is provided for STL/std::any compatibility.
\overload
*/
/*!
\fn template <typename Type, typename List, typename... Args, if_constructible<Type, std::initializer_list<List> &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t<Type>, std::initializer_list<List> il, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, std::initializer_list<List> &, Args... >::value)
\since 6.6
\overload
This overload exists to support types with constructors taking an
\c initializer_list. It behaves otherwise equivalent to the
non-initializer list \c{in_place_type_t} overload.
*/
QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface())
{
// we query the metatype instead of detecting it at compile time
// so that we can change relocatability of internal types
if (!Private::canUseInternalSpace(type.iface())) {
d.data.shared = PrivateShared::create(type.sizeOf(), type.alignOf());
d.is_shared = true;
}
}
/*!
\fn QVariant::QVariant(const QString &val) noexcept

View File

@ -62,6 +62,15 @@ template<> constexpr inline bool qIsRelocatable<QVariant> = true;
}
class Q_CORE_EXPORT QVariant
{
template <typename Type, typename... Args>
using if_constructible = std::enable_if_t<
std::conjunction_v<
std::is_copy_constructible<q20::remove_cvref_t<Type>>,
std::is_destructible<q20::remove_cvref_t<Type>>,
std::is_constructible<q20::remove_cvref_t<Type>, Args...>
>,
bool>;
struct CborValueStandIn { qint64 n; void *c; int t; };
public:
struct PrivateShared
@ -204,6 +213,37 @@ public:
explicit QVariant(QMetaType type, const void *copy = nullptr);
QVariant(const QVariant &other);
private:
template<typename Type, typename ...Args>
using is_noexcept_constructible = std::conjunction<
std::bool_constant<Private::CanUseInternalSpace<Type>>,
std::is_nothrow_constructible<Type, Args...>
>;
public:
template <typename Type, typename... Args,
if_constructible<Type, Args...> = true>
explicit QVariant(std::in_place_type_t<Type>, Args&&... args)
noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, Args...>::value)
: QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<Type>>() )
{
void *data = const_cast<void *>(constData());
new (data) Type(std::forward<Args>(args)...);
}
template <typename Type, typename List, typename... Args,
if_constructible<Type, std::initializer_list<List> &, Args...> = true>
explicit QVariant(std::in_place_type_t<Type>, std::initializer_list<List> il, Args&&... args)
noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>,
std::initializer_list<List> &,
Args...
>::value)
: QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<Type>>())
{
char *data = static_cast<char *>(const_cast<void *>(constData()));
new (data) Type(il, std::forward<Args>(args)...);
}
// primitives
QVariant(int i) noexcept;
QVariant(uint ui) noexcept;
@ -535,6 +575,9 @@ private:
// int variant, so delete this constructor:
QVariant(QMetaType::Type) = delete;
// used to setup the QVariant internals for the "real" inplace ctor
QVariant(std::in_place_t, QMetaType type);
// These constructors don't create QVariants of the type associated
// with the enum, as expected, but they would create a QVariant of
// type int with the value of the enum value.

View File

@ -378,6 +378,8 @@ private slots:
void constructFromIncompatibleMetaType();
void copyNonDefaultConstructible();
void inplaceConstruct();
void getIf_int() { getIf_impl(42); }
void getIf_QString() { getIf_impl(u"string"_s); };
void getIf_NonDefaultConstructible();
@ -5720,6 +5722,23 @@ void tst_QVariant::copyNonDefaultConstructible()
QCOMPARE(var2, var);
}
void tst_QVariant::inplaceConstruct()
{
{
NonDefaultConstructible ndc(42);
QVariant var(std::in_place_type<NonDefaultConstructible>, 42);
QVERIFY(get_if<NonDefaultConstructible>(&var));
QCOMPARE(get<NonDefaultConstructible>(var), ndc);
}
{
std::vector<int> vec {1, 2, 3, 4};
QVariant var(std::in_place_type<std::vector<int>>, {1, 2, 3, 4});
QVERIFY(get_if<std::vector<int>>(&var));
QCOMPARE(get<std::vector<int>>(var), vec);
}
}
void tst_QVariant::getIf_NonDefaultConstructible()
{
getIf_impl(NonDefaultConstructible{42});