From e6d9617c79ea029cf60c5555a3d7a32bac1cdbf6 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 3 Mar 2016 20:20:42 +0100 Subject: [PATCH] QHash/QMultiHash: add range constructors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMap and QMultiMap will go in a separate commit, due to QMap's insertion behavior that "reverses" the inserted elements. [ChangeLog][QtCore][QHash] Added range constructor. [ChangeLog][QtCore][QMultiHash] Added range constructor. Change-Id: Icfd0d0afde27792e8439ed6df3e8774696b134d3 Reviewed-by: Edward Welbourne Reviewed-by: Sérgio Martins Reviewed-by: Thiago Macieira --- src/corelib/tools/qcontainertools_impl.h | 43 ++++++ src/corelib/tools/qhash.cpp | 22 +++ src/corelib/tools/qhash.h | 43 ++++++ .../tst_containerapisymmetry.cpp | 140 ++++++++++++++++++ 4 files changed, 248 insertions(+) diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h index c2de50b145e..86a16eb32b7 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(std::distance(f, l))); } + +// for detecting expression validity +template +using void_t = void; + +template > +struct AssociativeIteratorHasKeyAndValue : std::false_type +{ +}; + +template +struct AssociativeIteratorHasKeyAndValue< + Iterator, + void_t().key()), + decltype(std::declval().value())> + > + : std::true_type +{ +}; + +template , typename = void_t<>> +struct AssociativeIteratorHasFirstAndSecond : std::false_type +{ +}; + +template +struct AssociativeIteratorHasFirstAndSecond< + Iterator, + void_t()->first), + decltype(std::declval()->second)> + > + : std::true_type +{ +}; + +template +using IfAssociativeIteratorHasKeyAndValue = + typename std::enable_if::value, bool>::type; + +template +using IfAssociativeIteratorHasFirstAndSecond = + typename std::enable_if::value, bool>::type; + } // namespace QtPrivate QT_END_NAMESPACE diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index dd22a38be1a..5c7e535c305 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 template QHash::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 QHash::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 template 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 QMultiHash::iterator QMultiHash::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 120ee9cc85d..a757e85386a 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -46,6 +46,7 @@ #include #include #include +#include #ifdef Q_COMPILER_INITIALIZER_LISTS #include @@ -258,6 +259,28 @@ public: QHash(QHash &&other) noexcept : d(other.d) { other.d = const_cast(&QHashData::shared_null); } QHash &operator=(QHash &&other) noexcept { QHash moved(std::move(other)); swap(moved); return *this; } +#endif +#ifdef Q_QDOC + template + QHash(InputIterator f, InputIterator l); +#else + template = true> + QHash(InputIterator f, InputIterator l) + : QHash() + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f.key(), f.value()); + } + + template = 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); } @@ -1028,6 +1051,26 @@ public: for (typename std::initializer_list >::const_iterator it = list.begin(); it != list.end(); ++it) insert(it->first, it->second); } +#endif +#ifdef Q_QDOC + template + QMultiHash(InputIterator f, InputIterator l); +#else + template = true> + QMultiHash(InputIterator f, InputIterator l) + { + QtPrivate::reserveIfForwardIterator(this, f, l); + for (; f != l; ++f) + insert(f.key(), f.value()); + } + + template = 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 196276c52f0..7df220acf9c 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 @@ -41,6 +42,7 @@ #include // for reference #include #include +#include // 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() #include #endif +#if COMPILER_HAS_STDLIB_INCLUDE() +#include +#endif struct Movable { @@ -255,6 +260,9 @@ private: template class Container> void non_associative_container_duplicates_strategy() const; + template + void ranged_ctor_associative_impl() const; + private Q_SLOTS: // non associative void ranged_ctor_std_vector_int() { ranged_ctor_non_associative_impl>(); } @@ -403,6 +411,71 @@ private Q_SLOTS: void ranged_ctor_QSet_Complex() { ranged_ctor_non_associative_impl>(); } void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + // associative + void ranged_ctor_std_map_int() { ranged_ctor_associative_impl>(); } + void ranged_ctor_std_map_Movable() { ranged_ctor_associative_impl>(); } + void ranged_ctor_std_map_Complex() { ranged_ctor_associative_impl>(); } + + void ranged_ctor_std_multimap_int() { ranged_ctor_associative_impl>(); } + void ranged_ctor_std_multimap_Movable() { ranged_ctor_associative_impl>(); } + void ranged_ctor_std_multimap_Complex() { ranged_ctor_associative_impl>(); } + + void ranged_ctor_unordered_map_int() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_unordered_map_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_unordered_map_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_QHash_int() { ranged_ctor_associative_impl>(); } + void ranged_ctor_QHash_Movable() { ranged_ctor_associative_impl>(); } + void ranged_ctor_QHash_Complex() { ranged_ctor_associative_impl>(); } + + void ranged_ctor_unordered_multimap_int() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_unordered_multimap_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_unordered_multimap_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_QMultiHash_int() { ranged_ctor_associative_impl>(); } + void ranged_ctor_QMultiHash_Movable() { ranged_ctor_associative_impl>(); } + void ranged_ctor_QMultiHash_Complex() { ranged_ctor_associative_impl>(); } + private: template void front_back_impl() const; @@ -651,6 +724,73 @@ void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() c } #endif // Q_COMPILER_INITIALIZER_LISTS +template +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. + + // plain array + const std::pair 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> 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> 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> 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 Container make(int size) {