QHash/QMultiHash: add range constructors

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 <edward.welbourne@qt.io>
Reviewed-by: Sérgio Martins <sergio.martins@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2016-03-03 20:20:42 +01:00 committed by Giuseppe D'Angelo
parent f10d37c9c1
commit e6d9617c79
4 changed files with 248 additions and 0 deletions

View File

@ -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

View File

@ -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.

View File

@ -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>
@ -258,6 +259,28 @@ public:
QHash(QHash &&other) noexcept : d(other.d) { other.d = const_cast<QHashData *>(&QHashData::shared_null); }
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); }
@ -1028,6 +1051,26 @@ public:
for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
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!

View File

@ -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;
@ -651,6 +724,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)
{