diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index 121c9fdcd8..8b31fb4a81 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -39,6 +39,8 @@ ****************************************************************************/ #include +#include +#include #ifndef QTYPEINFO_H #define QTYPEINFO_H @@ -322,5 +324,117 @@ Q_DECLARE_TYPEINFO(wchar_t, Q_RELOCATABLE_TYPE); # endif #endif // Qt 6 +namespace QTypeTraits +{ + +/* + The templates below aim to find out whether one can safely instantiate an operator==() or + operator<() for a type. + + This is tricky for containers, as most containers have unconstrained comparison operators, even though they + rely on the corresponding operators for its content. + This is especially true for all of the STL template classes that have a comparison operator defined, and + leads to the situation, that the compiler would try to instantiate the operator, and fail if any + of its template arguments does not have the operator implemented. + + The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type + of a container (if it exists), and checking the template arguments of pair, tuple and variant. +*/ +namespace detail { + +// find out whether T has a value_type typedef +// this is required to check the value type of containers for the existence of the comparison operator +template +struct has_value_type : std::false_type {}; +template +struct has_value_type> : std::true_type {}; + +// Checks the existence of the comparison operator for the class itself +template +struct has_operator_equal : std::false_type {}; +template +struct has_operator_equal() == std::declval()))>> + : std::true_type {}; + +// Two forward declarations +template::value> +struct expand_operator_equal_container; +template +struct expand_operator_equal_tuple; + +// the entry point for the public method +template +using expand_operator_equal = expand_operator_equal_container; + +// if T doesn't have a value_type member check if it's a tuple like object +template +struct expand_operator_equal_container : expand_operator_equal_tuple {}; +// if T::value_type exists, check first T::value_type, then T itself +template +struct expand_operator_equal_container : + std::conjunction, expand_operator_equal_tuple> {}; + +// recursively check the template arguments of a tuple like object +template +using expand_operator_equal_recursive = std::conjunction...>; + +template +struct expand_operator_equal_tuple : has_operator_equal {}; +template +struct expand_operator_equal_tuple> : expand_operator_equal_recursive {}; +template +struct expand_operator_equal_tuple> : expand_operator_equal_recursive {}; +template +struct expand_operator_equal_tuple> : expand_operator_equal_recursive {}; + +// the same for operator<(), see above for explanations +template +struct has_operator_less_than : std::false_type{}; +template +struct has_operator_less_than() < std::declval()))>> + : std::true_type{}; + +template::value> +struct expand_operator_less_than_container; +template +struct expand_operator_less_than_tuple; + +template +using expand_operator_less_than = expand_operator_less_than_container; + +template +struct expand_operator_less_than_container : expand_operator_less_than_tuple {}; +template +struct expand_operator_less_than_container : + std::conjunction, expand_operator_less_than_tuple> {}; + +template +using expand_operator_less_than_recursive = std::conjunction...>; + +template +struct expand_operator_less_than_tuple : has_operator_less_than {}; +template +struct expand_operator_less_than_tuple> : expand_operator_less_than_recursive {}; +template +struct expand_operator_less_than_tuple> : expand_operator_less_than_recursive {}; +template +struct expand_operator_less_than_tuple> : expand_operator_less_than_recursive {}; + +} + +template +struct has_operator_equal : detail::expand_operator_equal {}; +template +constexpr bool has_operator_equal_v = has_operator_equal::value; + +template +struct has_operator_less_than : detail::expand_operator_less_than {}; +template +constexpr bool has_operator_less_than_v = has_operator_less_than::value; + + +} + + QT_END_NAMESPACE #endif // QTYPEINFO_H diff --git a/src/corelib/tools/qcontainerfwd.h b/src/corelib/tools/qcontainerfwd.h index 715583e03b..4aec3574ff 100644 --- a/src/corelib/tools/qcontainerfwd.h +++ b/src/corelib/tools/qcontainerfwd.h @@ -42,8 +42,11 @@ #include -QT_BEGIN_NAMESPACE +// std headers can unfortunately not be forward declared +#include +#include +QT_BEGIN_NAMESPACE template class QCache; template class QHash; diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 848ba5c4d4..a44f0088d3 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -567,6 +567,7 @@ public: // STL compatibility typedef Key key_type; typedef T mapped_type; + typedef T value_type; typedef qptrdiff difference_type; typedef qsizetype size_type; inline bool empty() const { return isEmpty(); } diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 8d4cc05eb9..22d481f5e3 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -48,6 +48,103 @@ Q_DECLARE_METATYPE(QMetaType::Type) +namespace CheckTypeTraits +{ +struct NoOperators +{ + int x; +}; +using Nested = QVector>>; +using Nested2 = QVector>>>; + +// basic types +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); + +// no comparison operators +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_less_than_v); + +// standard Qt types +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_less_than_v); + +// QList +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// QPair +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// QMap +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// QHash +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// std::vector +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// std::pair +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// std::tuple +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// std::map +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(QTypeTraits::has_operator_less_than_v>); +static_assert(!QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +// nested types +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); +static_assert(QTypeTraits::has_operator_equal_v>); +static_assert(!QTypeTraits::has_operator_less_than_v>); + +} + + class tst_QMetaType: public QObject { Q_OBJECT