一个万用的Hash Function
目标: 为Customer写一个CustomerHash, 使我们在用Customer的unodered_set时得以提供自定义的hash functor.
struct Customer {
string fname; // first name
string lname; // last name
long no;
};
class CustomerHash {
public:
std::size_t operator()(const Customer &c) const {
return ...;
}
};
unordered_set custset;
CustomerHash的实现:
// STEP 4:
// from boost (functional/hash)
template
inline void hash_combine (size_t &seed, const T &val)
{
seed ^= hash()(val) +
0x9e3779b9 +
(seed<<6) +
(seed>>2);
}
// STEP 3:
// auxiliary generic functions to create a hash value using a seed
template
inline void hash_val (size_t &seed, const T& val)
{
hash_combine(seed, val);
}
// STEP 2:
template
inline void hash_val(size_t &seed,
const T &val,
const Types&... args)
{
hash_combine(seed, val);
hash_val(seed, args...);
}
// STEP 1:
// auxiliary generic function
template
inline size_t hash_val (const Type&... args)
{
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
class CustomerHash {
public:
size_t operator() (const Customer &c) const {
return hash_val(c.fname, c.lname, c.no);
}
};
以struct hash特化形式实现Hash Function
课件上的一个简单例子
class MyString {
private:
char* _data;
size_t _len;
// ...
};
namespace std // 必须放在std内
{
template<>
struct hash 为了unordered containers
{
size_t operator()(const MyString &s) const noexcept
{ return hash()(string(s.get())); } // 借用hash
};
}
关于hash的特话(摘自C++ Primer):
In addition to specializing function templates, we can also specialize class templates. As an example, we’ll define a specialization of the library
hash
template that we can use to storeSales_data
objects in an unordered container. By default, the unordered containers usehash
to organize their elements. To use this default with our own data type, we must define a specialization of thehash
template. A specializedhash
class must define
- An overloaded call operator that returns a
size_t
and takes an object of the container’s key type- Two type members,
result_type
andargument_type
, which are the return and argument types, respectively, of the call operator- The default constructor and a copy-assignment operator (which can be implicitly defined).
The only complication in defining this hash specialization is that when we specialize a template, we must do so in the same namespace in which the original template is defined.
评论: C++ Primer的描述与课件基本一致, 不同点在于书中要求必须在hash的特化版本中定义result_type
和argument_type
. 详见另一个更复杂的例子(摘自C++ Primer):
// open the std namespace so we can specialize std::hash
namespace std {
template <> // we're defining a specialization with
struct hash // the template parameter of Sales_data
{
// the type used to hash an unordered container
// must define these types
typedef size_t result_type;
typedef Sales_data argument_type; // by default, this type needs ==
size_t operator()(const Sales_data& s) const;
// our class uses synthesized copy control and default constructor
};
size_t hash::operator()(const Sales_data& s) const
{
return hash()(s.bookNo) ^
hash()(s.units_sold) ^
hash()(s.revenue);
}
} // close the std namespace; note: no semicolon after the close curly
tuple, 用例
// tuples
// create a four-element tuple
// - elements are initialized with default value
// (0 for fundamental types)
tuple> t;
cout << "sizeof = " << sizeof(t) << endl;
// create and initialize a tuple explicitly
tuple t1(41, 6.3, "nico");
// iterate over elements:
cout << "t1: " << get<0>(t1) << ' '
<< get<1>(t1) << ' '
<< get<2>(t1) << endl;
关于tuple (摘自C++ Primer):
A tuple is a template that is similar to a pair. Each pair type has different types for its members, but every pair always has exactly two members. A tuple also has members whose types vary from one tuple type to another, but a tuple can have any number of members. Each distinct tuple type has a fixed number of members, but the number of members in one tuple type can differ from the number of members in another.
A tuple is most useful when we want to combine some data into a single object but do not want to bother to define a data structure to represent those data. The Table lists the operations that tuples support. The tuple type, along with its companion types and functions, are defined in thetuple
header.
type traits (G2.9)
struct __true_type{};
struct __false_type{};
template
struct __type_traits {
typedef __true_type this_dummy_member_must_be_first;
typedef __false_type has_trivial_default_constructor;
typedef __false_type has_trivial_copy_constructor;
typedef __false_type has_trivial_assignment_operator;
typedef __false_type has_trivial_destructor;
typedef __false_type is_POD_type; // Plain Old Data
};
template <> struct __type_traits {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type; // Plain Old Data
};
template <> struct __type_traits {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type; // Plain Old Data
};
__type_traits在SGI STL中的应用很广. 这里给出一个例子(摘自 STL源码剖析):
uninitialized_fill_n() 全局函数:
template
inline ForwardIterator uninitialized_fill_n(ForwardIterator first,
Size n, const T &x) {
return __unitialized_fill_n(first, n, x, value_type(first));
}
该函数以x为蓝本, 自迭代器first开始构造n个元素. 为取得最大效率, 首先以value_type()萃取出迭代器first的value type, 再利用__type_traits 判断该类型是否为POD类别:
template
inline ForwardIterator __uninitialized_fill_n(ForwardIterator first,
Size n, const T &x, T1*)
{
typedef typename __type_traits::is_POD_type is_POD;
return __uninitialized_fill_n_aux(frist, n, x, is_POD());
}
以下就"是否为POD类别"采取最适当的措施:
// 如果不是POD型别, 就会派送(dispatch)到这里
template
ForwardIterator
__uninitialized_fill_n_aux(ForwardIterator first, Size n,
const T &x, __false_type) {
ForwardIterator cur = first;
for (; n > 0; --n, ++cur) construct(&*cur x);
return cur;
}
// 如果是POD型别, 就会派送(dispatch)到这里.
template
ForwardIterator
__uninitialized_fill_n_aux(ForwardIterator first, Size n,
const T &x, __true_type) {
return fill_n(first, n, x); // 交由高阶函数执行
}
type traits (since C++11)
关于type traits (摘自 The C++ Programming Language):
In
, the standard library provides type functions to determine properties of types and to generate new types from existing ones. These type functions are primarily used at compile time to support simple, and not so simple, metaprogramming.
type traits, 实现is_void
/// remove_const
template
struct remove_const
{ typedef _Tp type; };
template
struct remove_const <_Tp const>
{ typedef _Tp type; };
/// remove_volatile
template
struct remove_volatile
{ typedef _Tp type; };
template
struct remove_volatile <_Tp volatile>
{ typedef _Tp type; };
/// remove_cv
template
struct remove_cv
{
typedef typename
remove_const::type>::type type;
};
template
struct __is_void_helper : public false_type {};
template <>
struct __is_void_helper : public true_type {};
/// is_void
template
struct is_void : public __is_void_helper::type>::type
{};
type traits, 实现is_integral
template
struct __is_integral_helper : public false_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
template <>
struct __is_integral_helper
: public true_type {};
/// is_integral
template
struct is_integral
: public __is_integral_helper::type>::type
{};
Move Constructor & Move Assignment
摘自C++ Primer
Like the string class (and other library classes), our own classes can benefit from being able to be moved as well as copied. To enable move operations for our own types, we define a move constructor and a move-assignment operator. These members are similar to the corresponding copy operations, but they “steal” resources from their given object rather than copy them.