//2020年07月07日 22时26分32秒
#include <cstddef>
#include <vector>
#include <list>
#include <string>
#include <functional>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <algorithm>
#include <utility>
class MoveableClass //p492
{
public:
MoveableClass() {
std::cout << "Default constructor" << std::endl;
}
MoveableClass(const MoveableClass& /* src */) {
std::cout << "Copy constructor" << std::endl;
}
MoveableClass(MoveableClass&& /* src */) noexcept {
std::cout << "Move constructor" << std::endl;
}
MoveableClass& operator=(const MoveableClass& /* rhs */) {
std::cout << "Copy assignment operator" << std::endl;
return *this;
}
MoveableClass& operator=(MoveableClass&& /* rhs */) noexcept {
std::cout << "Move assignment operator" << std::endl;
return *this;
}
};
template <typename InputIterator, typename OutputIterator, typename Predicate>
OutputIterator find_all(InputIterator first, InputIterator last,
OutputIterator dest, Predicate pred)
{
while (first != last) {
if (pred(*first)) {
*dest = first;
++dest;
}
++first;
}
return dest;
}
template <typename IteratorType>
void iteratorTraitsTest(IteratorType it)
{
typename std::iterator_traits<IteratorType>::value_type temp;
temp = *it;
//auto temp = *it;
std::cout << temp << std::endl;
}
namespace ProCpp {
// A default hash object
template <typename T>
class hash
{
public:
size_t operator()(const T& key) const;
};
// A hash specialization for strings
template <>
class hash<std::string>
{
public:
size_t operator()(const std::string& key) const;
};
template <typename Key, typename T, //p740
typename KeyEqual = std::equal_to<>,
typename Hash = hash<Key>>
class hash_map
{
public:
using key_type = Key;
using mapped_type = T;
using value_type = std::pair<const Key, T>;
// Virtual destructor
virtual ~hash_map() = default;
// Throws invalid_argument if the number of buckets is illegal.
explicit hash_map(const KeyEqual& equal = KeyEqual(),
size_t numBuckets = 101, const Hash& hash = Hash());
// Copy constructor
hash_map(const hash_map<Key, T, KeyEqual, Hash>& src) = default;
// Move constructor
hash_map(hash_map<Key, T, KeyEqual, Hash>&& src) noexcept = default;
// Copy assignment operator
hash_map<Key, T, KeyEqual, Hash>& operator=(const hash_map<Key, T, KeyEqual, Hash>& rhs);
// Move assignment operator
hash_map<Key, T, KeyEqual, Hash>& operator=(hash_map<Key, T, KeyEqual, Hash>&& rhs) noexcept;
// Inserts the key/value pair x.
void insert(const value_type& x);
// Removes the element with key k, if it exists.
void erase(const key_type& k);
// Removes all elements.
void clear() noexcept;
// Find returns a pointer to the element with key k.
// Returns nullptr if no element with that key exists.
value_type* find(const key_type& k);
const value_type* find(const key_type& k) const;
// operator[] finds the element with key k, or inserts an
// element with that key if none exists yet. Returns a reference to
// the value corresponding to that key.
T& operator[] (const key_type& k);
// Swaps two hash_maps.
void swap(hash_map<Key, T, KeyEqual, Hash>& other) noexcept;
private:
using ListType = std::list<value_type>;
// Returns a pair containing an iterator to the found element with a given key,
// and the index of that element's bucket.
std::pair<typename ListType::iterator, size_t> findElement(const key_type& k);
std::vector<ListType> mBuckets;
size_t mSize = 0;
KeyEqual mEqual;
Hash mHash;
};
}
//professional c++ 4th edition p742
// Calculate a hash by treating the key as a sequence
// of bytes and summing the ASCII values of the bytes.
template <typename T>
size_t ProCpp::hash<T>::operator()(const T& key) const
{
const size_t bytes = sizeof(key);
size_t sum = 0;
for (size_t i = 0; i < bytes; ++i) {
unsigned char b = *(reinterpret_cast<const unsigned char*>(&key) + i);
sum += b;
}
return sum;
}
// Calculate a hash by summing the ASCII values of all characters.
size_t ProCpp::hash<std::string>::operator()(const std::string& key) const
{
size_t sum = 0;
for (auto c : key) {
sum += static_cast<unsigned char>(c);
}
return sum;
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
void swap(ProCpp::hash_map<Key, T, KeyEqual, Hash>& first, ProCpp::hash_map<Key, T, KeyEqual, Hash>& second) noexcept
{
first.swap(second);
}
// Construct mBuckets with the correct number of buckets.
template <typename Key, typename T, typename KeyEqual, typename Hash>
ProCpp::hash_map<Key, T, KeyEqual, Hash>::hash_map(const KeyEqual& equal, size_t numBuckets, const Hash& hash)
: mBuckets(numBuckets), mEqual(equal), mHash(hash)
{
if (numBuckets == 0) {
throw std::invalid_argument("Number of buckets must be positive");
}
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
ProCpp::hash_map<Key, T, KeyEqual, Hash>& ProCpp::hash_map<Key, T, KeyEqual, Hash>::operator=(const hash_map<Key, T, KeyEqual, Hash>& rhs)
{
// check for self-assignment
if (this == &rhs) {
return *this;
}
// Copy-and-swap idiom
auto copy = rhs; // Do all the work in a temporary instance
swap(copy); // Commit the work with only non-throwing operations
return *this;
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
ProCpp::hash_map<Key, T, KeyEqual, Hash>& ProCpp::hash_map<Key, T, KeyEqual, Hash>::operator=(hash_map<Key, T, KeyEqual, Hash>&& rhs) noexcept
{
swap(rhs);
return *this;
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
std::pair<typename ProCpp::hash_map<Key, T, KeyEqual, Hash>::ListType::iterator, size_t>
ProCpp::hash_map<Key, T, KeyEqual, Hash>::findElement(const key_type& k)
{
// Hash the key to get the bucket.
size_t bucket = mHash(k) % mBuckets.size();
// Search for the key in the bucket.
auto iter = find_if(begin(mBuckets[bucket]), end(mBuckets[bucket]),
[this, &k](const auto& element) { return mEqual(element.first, k); });
// Return a pair of the iterator and the bucket index.
return std::make_pair(iter, bucket);
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
typename ProCpp::hash_map<Key, T, KeyEqual, Hash>::value_type* ProCpp::hash_map<Key, T, KeyEqual, Hash>::find(const key_type& k)
{
// Use the findElement() helper, and C++17 structured bindings.
auto[it, bucket] = findElement(k);
if (it == end(mBuckets[bucket])) {
// Element not found -- return nullptr.
return nullptr;
}
// Element found -- return a pointer to it.
return &(*it);
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
const typename ProCpp::hash_map<Key, T, KeyEqual, Hash>::value_type* ProCpp::hash_map<Key, T, KeyEqual, Hash>::find(const key_type& k) const
{
return const_cast<hash_map<Key, T, KeyEqual, Hash>*>(this)->find(k);
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
T& ProCpp::hash_map<Key, T, KeyEqual, Hash>::operator[] (const key_type& k)
{
// Try to find the element. If it doesn't exist, add a new element.
auto[it, bucket] = findElement(k);
if (it == end(mBuckets[bucket])) {
mSize++;
mBuckets[bucket].push_back(std::make_pair(k, T()));
return mBuckets[bucket].back().second;
} else {
return it->second;
}
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
void ProCpp::hash_map<Key, T, KeyEqual, Hash>::insert(const value_type& x)
{
// Try to find the element.
auto[it, bucket] = findElement(x.first);
if (it != end(mBuckets[bucket])) {
// The element already exists.
return;
} else {
// We didn't find the element, so insert a new one.
mSize++;
mBuckets[bucket].push_back(x);
}
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
void ProCpp::hash_map<Key, T, KeyEqual, Hash>::erase(const key_type& k)
{
// First, try to find the element.
auto[it, bucket] = findElement(k);
if (it != end(mBuckets[bucket])) {
// The element exists -- erase it.
mBuckets[bucket].erase(it);
mSize--;
}
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
void ProCpp::hash_map<Key, T, KeyEqual, Hash>::clear() noexcept
{
// Call clear on each bucket.
for (auto& bucket : mBuckets) {
bucket.clear();
}
mSize = 0;
}
template <typename Key, typename T, typename KeyEqual, typename Hash>
void ProCpp::hash_map<Key, T, KeyEqual, Hash>::swap(hash_map<Key, T, KeyEqual, Hash>& other) noexcept
{
using std::swap;
swap(mBuckets, other.mBuckets);
swap(mSize, other.mSize);
swap(mEqual, other.mEqual);
swap(mHash, other.mHash);
}
int main()
{
std::vector<MoveableClass> vecSource;
MoveableClass mc;
vecSource.push_back(mc);
vecSource.push_back(mc);
std::cout << "----" << std::endl;
//Without using move_iterators, this code triggers the copy constructor two times, once for every element
//in vecSource p763
// Copy the elements from vecSource to vecOne
std::vector<MoveableClass> vecOne(cbegin(vecSource), cend(vecSource));
std::cout << "----" << std::endl;
// Move the elements from vecSource to vecTwo
std::vector<MoveableClass> vecTwo(std::make_move_iterator(begin(vecSource)),
std::make_move_iterator(end(vecSource)));
// Using C++17's template argument deduction for constructors.
//vector<MoveableClass> vecTwo(move_iterator(begin(vecSource)),
// move_iterator(end(vecSource)));
std::cout << "----" << std::endl;
std::vector<int> vec{ 3, 4, 5, 4, 5, 6, 5, 8 };
std::vector<std::vector<int>::iterator> matches;
find_all(begin(vec), end(vec), back_inserter(matches), [](int i){ return i == 5; });
std::cout << "Found " << matches.size() << " matching elements: " << std::endl;
for (const auto& it : matches) {
std::cout << *it << " at position " << (it - cbegin(vec)) << std::endl;;
}
std::cout << std::endl;
std::vector<int> v{ 5 };
iteratorTraitsTest(cbegin(v));
ProCpp::hash_map<int, int> myHash;
myHash.insert(std::make_pair(4, 40));
myHash.insert(std::make_pair(6, 60));
// x will have type hash_map<int, int>::value_type*
auto x = myHash.find(4);
if (x != nullptr) {
std::cout << "4 maps to " << x->second << std::endl;
} else {
std::cout << "cannot find 4 in map" << std::endl;
}
myHash.erase(4);
auto x2 = myHash.find(4);
if (x2 != nullptr) {
std::cout << "4 maps to " << x2->second << std::endl;
} else {
std::cout << "cannot find 4 in map" << std::endl;
}
myHash[4] = 35;
myHash[4] = 60;
auto x3 = myHash.find(4);
if (x3 != nullptr) {
std::cout << "4 maps to " << x3->second << std::endl;
} else {
std::cout << "cannot find 4 in map" << std::endl;
}
// Test std::swap().
ProCpp::hash_map<int, int> other(std::equal_to<>(), 11);
swap(other, myHash);
// Test copy construction and copy assignment.
ProCpp::hash_map<int, int> myHash2(other);
ProCpp::hash_map<int, int> myHash3;
myHash3 = myHash2;
// Test move construction and move assignment.
ProCpp::hash_map<int, int> myHash4(std::move(myHash3));
ProCpp::hash_map<int, int> myHash5;
myHash5 = std::move(myHash4);
return 0;
}
/*
wannian07@wannian07-PC:~$ g++ -std=c++17 -o c13 c13.cpp -pthread
wannian07@wannian07-PC:~$ ./c13
Default constructor
Copy constructor
Copy constructor
Move constructor
----
Copy constructor
Copy constructor
----
Move constructor
Move constructor
----
Found 3 matching elements:
5 at position 2
5 at position 4
5 at position 6
5
4 maps to 40
cannot find 4 in map
4 maps to 60
*/