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:
parent
f10d37c9c1
commit
e6d9617c79
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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!
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user