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)));
|
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
|
} // namespace QtPrivate
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -1251,6 +1251,17 @@ uint qHash(long double key, uint seed) noexcept
|
|||||||
compiled in C++11 mode.
|
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)
|
/*! \fn template <class Key, class T> QHash<Key, T>::QHash(const QHash &other)
|
||||||
|
|
||||||
Constructs a copy of \a other.
|
Constructs a copy of \a other.
|
||||||
@ -2586,6 +2597,17 @@ uint qHash(long double key, uint seed) noexcept
|
|||||||
\sa operator=()
|
\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)
|
/*! \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.
|
Inserts a new item with the \a key and a value of \a value.
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <QtCore/qlist.h>
|
#include <QtCore/qlist.h>
|
||||||
#include <QtCore/qrefcount.h>
|
#include <QtCore/qrefcount.h>
|
||||||
#include <QtCore/qhashfunctions.h>
|
#include <QtCore/qhashfunctions.h>
|
||||||
|
#include <QtCore/qcontainertools_impl.h>
|
||||||
|
|
||||||
#ifdef Q_COMPILER_INITIALIZER_LISTS
|
#ifdef Q_COMPILER_INITIALIZER_LISTS
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@ -258,6 +259,28 @@ public:
|
|||||||
QHash(QHash &&other) noexcept : d(other.d) { other.d = const_cast<QHashData *>(&QHashData::shared_null); }
|
QHash(QHash &&other) noexcept : d(other.d) { other.d = const_cast<QHashData *>(&QHashData::shared_null); }
|
||||||
QHash &operator=(QHash &&other) noexcept
|
QHash &operator=(QHash &&other) noexcept
|
||||||
{ QHash moved(std::move(other)); swap(moved); return *this; }
|
{ 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
|
#endif
|
||||||
void swap(QHash &other) noexcept { qSwap(d, other.d); }
|
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)
|
for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
|
||||||
insert(it->first, it->second);
|
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
|
#endif
|
||||||
// compiler-generated copy/move ctors/assignment operators are fine!
|
// compiler-generated copy/move ctors/assignment operators are fine!
|
||||||
// compiler-generated destructor is fine!
|
// compiler-generated destructor is fine!
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "qstring.h"
|
#include "qstring.h"
|
||||||
#include "qvarlengtharray.h"
|
#include "qvarlengtharray.h"
|
||||||
#include "qvector.h"
|
#include "qvector.h"
|
||||||
|
#include "qhash.h"
|
||||||
#include "qdebug.h"
|
#include "qdebug.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -41,6 +42,7 @@
|
|||||||
#include <vector> // for reference
|
#include <vector> // for reference
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
// MSVC has these containers from the Standard Library, but it lacks
|
// MSVC has these containers from the Standard Library, but it lacks
|
||||||
// a __has_include mechanism (that we need to use for other stdlibs).
|
// a __has_include mechanism (that we need to use for other stdlibs).
|
||||||
@ -58,6 +60,9 @@
|
|||||||
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
|
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#endif
|
#endif
|
||||||
|
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
|
||||||
|
#include <unordered_map>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Movable
|
struct Movable
|
||||||
{
|
{
|
||||||
@ -255,6 +260,9 @@ private:
|
|||||||
template<template<typename ... T> class Container>
|
template<template<typename ... T> class Container>
|
||||||
void non_associative_container_duplicates_strategy() const;
|
void non_associative_container_duplicates_strategy() const;
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
void ranged_ctor_associative_impl() const;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
// non associative
|
// non associative
|
||||||
void ranged_ctor_std_vector_int() { ranged_ctor_non_associative_impl<std::vector<int>>(); }
|
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_Complex() { ranged_ctor_non_associative_impl<QSet<Complex>>(); }
|
||||||
void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy<QSet>(); }
|
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:
|
private:
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
void front_back_impl() const;
|
void front_back_impl() const;
|
||||||
@ -651,6 +724,73 @@ void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() c
|
|||||||
}
|
}
|
||||||
#endif // Q_COMPILER_INITIALIZER_LISTS
|
#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>
|
template <typename Container>
|
||||||
Container make(int size)
|
Container make(int size)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user