diff options
-rw-r--r-- | src/corelib/tools/qcontainertools_impl.h | 43 | ||||
-rw-r--r-- | src/corelib/tools/qhash.cpp | 22 | ||||
-rw-r--r-- | src/corelib/tools/qhash.h | 43 | ||||
-rw-r--r-- | tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp | 140 |
4 files changed, 248 insertions, 0 deletions
diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h index c2de50b145..86a16eb32b 100644 --- a/src/corelib/tools/qcontainertools_impl.h +++ b/src/corelib/tools/qcontainertools_impl.h @@ -84,6 +84,49 @@ void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l { c->reserve(static_cast<typename Container::size_type>(std::distance(f, l))); } + +// for detecting expression validity +template <typename ... T> +using void_t = void; + +template <typename Iterator, typename = void_t<>> +struct AssociativeIteratorHasKeyAndValue : std::false_type +{ +}; + +template <typename Iterator> +struct AssociativeIteratorHasKeyAndValue< + Iterator, + void_t<decltype(std::declval<Iterator &>().key()), + decltype(std::declval<Iterator &>().value())> + > + : std::true_type +{ +}; + +template <typename Iterator, typename = void_t<>, typename = void_t<>> +struct AssociativeIteratorHasFirstAndSecond : std::false_type +{ +}; + +template <typename Iterator> +struct AssociativeIteratorHasFirstAndSecond< + Iterator, + void_t<decltype(std::declval<Iterator &>()->first), + decltype(std::declval<Iterator &>()->second)> + > + : std::true_type +{ +}; + +template <typename Iterator> +using IfAssociativeIteratorHasKeyAndValue = + typename std::enable_if<AssociativeIteratorHasKeyAndValue<Iterator>::value, bool>::type; + +template <typename Iterator> +using IfAssociativeIteratorHasFirstAndSecond = + typename std::enable_if<AssociativeIteratorHasFirstAndSecond<Iterator>::value, bool>::type; + } // namespace QtPrivate QT_END_NAMESPACE diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index dd22a38be1..5c7e535c30 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1251,6 +1251,17 @@ uint qHash(long double key, uint seed) noexcept compiled in C++11 mode. */ +/*! \fn template <class Key, class T> template <class InputIterator> QHash<Key, T>::QHash(InputIterator begin, InputIterator end) + \since 5.14 + + Constructs a hash with a copy of each of the elements in the iterator range + [\a begin, \a end). Either the elements iterated by the range must be + objects with \c{first} and \c{second} data members (like \c{QPair}, + \c{std::pair}, etc.) convertible to \c Key and to \c T respectively; or the + iterators must have \c{key()} and \c{value()} member functions, returning a + key convertible to \c Key and a value convertible to \c T respectively. +*/ + /*! \fn template <class Key, class T> QHash<Key, T>::QHash(const QHash &other) Constructs a copy of \a other. @@ -2586,6 +2597,17 @@ uint qHash(long double key, uint seed) noexcept \sa operator=() */ +/*! \fn template <class Key, class T> template <class InputIterator> QMultiHash::QMultiHash(InputIterator begin, InputIterator end) + \since 5.14 + + Constructs a multi-hash with a copy of each of the elements in the iterator range + [\a begin, \a end). Either the elements iterated by the range must be + objects with \c{first} and \c{second} data members (like \c{QPair}, + \c{std::pair}, etc.) convertible to \c Key and to \c T respectively; or the + iterators must have \c{key()} and \c{value()} member functions, returning a + key convertible to \c Key and a value convertible to \c T respectively. +*/ + /*! \fn template <class Key, class T> QMultiHash<Key, T>::iterator QMultiHash<Key, T>::replace(const Key &key, const T &value) Inserts a new item with the \a key and a value of \a value. diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 120ee9cc85..a757e85386 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -46,6 +46,7 @@ #include <QtCore/qlist.h> #include <QtCore/qrefcount.h> #include <QtCore/qhashfunctions.h> +#include <QtCore/qcontainertools_impl.h> #ifdef Q_COMPILER_INITIALIZER_LISTS #include <initializer_list> @@ -259,6 +260,28 @@ public: QHash &operator=(QHash &&other) noexcept { QHash moved(std::move(other)); swap(moved); return *this; } #endif +#ifdef Q_QDOC + template <typename InputIterator> + QHash(InputIterator f, InputIterator l); +#else + template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true> + QHash(InputIterator f, InputIterator l) + : QHash() + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f.key(), f.value()); + } + + template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true> + QHash(InputIterator f, InputIterator l) + : QHash() + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f->first, f->second); + } +#endif void swap(QHash &other) noexcept { qSwap(d, other.d); } bool operator==(const QHash &other) const; @@ -1029,6 +1052,26 @@ public: insert(it->first, it->second); } #endif +#ifdef Q_QDOC + template <typename InputIterator> + QMultiHash(InputIterator f, InputIterator l); +#else + template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true> + QMultiHash(InputIterator f, InputIterator l) + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f.key(), f.value()); + } + + template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true> + QMultiHash(InputIterator f, InputIterator l) + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f->first, f->second); + } +#endif // compiler-generated copy/move ctors/assignment operators are fine! // compiler-generated destructor is fine! diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 196276c52f..7df220acf9 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -34,6 +34,7 @@ #include "qstring.h" #include "qvarlengtharray.h" #include "qvector.h" +#include "qhash.h" #include "qdebug.h" #include <algorithm> @@ -41,6 +42,7 @@ #include <vector> // for reference #include <list> #include <set> +#include <map> // MSVC has these containers from the Standard Library, but it lacks // a __has_include mechanism (that we need to use for other stdlibs). @@ -58,6 +60,9 @@ #if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>) #include <unordered_set> #endif +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) +#include <unordered_map> +#endif struct Movable { @@ -255,6 +260,9 @@ private: template<template<typename ... T> class Container> void non_associative_container_duplicates_strategy() const; + template <typename Container> + void ranged_ctor_associative_impl() const; + private Q_SLOTS: // non associative void ranged_ctor_std_vector_int() { ranged_ctor_non_associative_impl<std::vector<int>>(); } @@ -403,6 +411,71 @@ private Q_SLOTS: void ranged_ctor_QSet_Complex() { ranged_ctor_non_associative_impl<QSet<Complex>>(); } void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy<QSet>(); } + // associative + void ranged_ctor_std_map_int() { ranged_ctor_associative_impl<std::map<int, int>>(); } + void ranged_ctor_std_map_Movable() { ranged_ctor_associative_impl<std::map<Movable, int>>(); } + void ranged_ctor_std_map_Complex() { ranged_ctor_associative_impl<std::map<Complex, int>>(); } + + void ranged_ctor_std_multimap_int() { ranged_ctor_associative_impl<std::multimap<int, int>>(); } + void ranged_ctor_std_multimap_Movable() { ranged_ctor_associative_impl<std::multimap<Movable, int>>(); } + void ranged_ctor_std_multimap_Complex() { ranged_ctor_associative_impl<std::multimap<Complex, int>>(); } + + void ranged_ctor_unordered_map_int() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_map<int, int>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_unordered_map_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_unordered_map_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_QHash_int() { ranged_ctor_associative_impl<QHash<int, int>>(); } + void ranged_ctor_QHash_Movable() { ranged_ctor_associative_impl<QHash<Movable, int>>(); } + void ranged_ctor_QHash_Complex() { ranged_ctor_associative_impl<QHash<Complex, int>>(); } + + void ranged_ctor_unordered_multimap_int() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_multimap<int, int>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_unordered_multimap_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_unordered_multimap_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>) + ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>(); +#else + QSKIP("<unordered_map> is needed for this test"); +#endif + } + + void ranged_ctor_QMultiHash_int() { ranged_ctor_associative_impl<QMultiHash<int, int>>(); } + void ranged_ctor_QMultiHash_Movable() { ranged_ctor_associative_impl<QMultiHash<Movable, int>>(); } + void ranged_ctor_QMultiHash_Complex() { ranged_ctor_associative_impl<QMultiHash<Complex, int>>(); } + private: template <typename Container> void front_back_impl() const; @@ -652,6 +725,73 @@ void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() c #endif // Q_COMPILER_INITIALIZER_LISTS template <typename Container> +void tst_ContainerApiSymmetry::ranged_ctor_associative_impl() const +{ + using K = typename Container::key_type; + using V = typename Container::mapped_type; + + // The double K(0) is deliberate. The order of the elements matters: + // * for unique-key STL containers, the first one should be the one inserted (cf. LWG 2844) + // * for unique-key Qt containers, the last one should be the one inserted + // * for multi-key sorted containers, the order of insertion of identical keys is also the + // iteration order (which establishes the equality of the containers) + // (although nothing of this is being tested here, that deserves its own testing) + const Container reference{ + { K(0), V(1000) }, + { K(1), V(1001) }, + { K(2), V(1002) }, + { K(0), V(1003) } + }; + + // Note that using anything not convertible to std::pair doesn't work for + // std containers. Their ranged construction is defined in terms of + // insert(value_type), which for std associative containers is + // std::pair<const K, T>. + + // plain array + const std::pair<K, V> values1[] = { + std::make_pair(K(0), V(1000)), + std::make_pair(K(1), V(1001)), + std::make_pair(K(2), V(1002)), + std::make_pair(K(0), V(1003)) + }; + + const Container c1(values1, values1 + sizeof(values1)/sizeof(values1[0])); + + // from QList + QList<std::pair<K, V>> l2; + l2 << std::make_pair(K(0), V(1000)) + << std::make_pair(K(1), V(1001)) + << std::make_pair(K(2), V(1002)) + << std::make_pair(K(0), V(1003)); + + const Container c2a(l2.begin(), l2.end()); + const Container c2b(l2.cbegin(), l2.cend()); + + // from std::list + std::list<std::pair<K, V>> l3; + l3.push_back(std::make_pair(K(0), V(1000))); + l3.push_back(std::make_pair(K(1), V(1001))); + l3.push_back(std::make_pair(K(2), V(1002))); + l3.push_back(std::make_pair(K(0), V(1003))); + const Container c3a(l3.begin(), l3.end()); + + // from const std::list + const std::list<std::pair<K, V>> l3c = l3; + const Container c3b(l3c.begin(), l3c.end()); + + // from itself + const Container c4(reference.begin(), reference.end()); + + QCOMPARE(c1, reference); + QCOMPARE(c2a, reference); + QCOMPARE(c2b, reference); + QCOMPARE(c3a, reference); + QCOMPARE(c3b, reference); + QCOMPARE(c4, reference); +} + +template <typename Container> Container make(int size) { Container c; |