通常在产生对象的hash码时,许多人会将对象中各个类型的元素取得hash码后相加得出该元素的hash码。这样作除了简单没有任何依据,根据实际中的应用,会发现这种方法产生相同的hash码可能性很大。因此C++提出了一种产生hash码的方法。ios
class CustomerHash{ public: std::size_t operator()(const Customer& c) const{ return hash_val(c.fname, c.lname, c.no); } }; template <typename... Types> inline size_t hash_val(const Types&... args){ size_t seed = 0; hash_val(seed, args...); return seed; } template <typename T, typebname... Types> inlne void hash_val(size_t& seed, const T& val, const Types&... args){ //逐一取出第一参数进行hash_combine来改变seed hash_combine(seed, val); hash_val(seed, args...); } template <typename T> inline void hash_combine(size_t& seed, const T& val){ seed ^=std::hash<T>()(val) + 0x9e3779b9 + (seed<<6) + (seed >> 2); } template <typename T> inline void hash_val(size_t& seed, const T& val){ hash_combine(seed, val); }
tuple
至关是一个可以容纳元素集合的对象。每一个元素能够是不一样的类型。下面来看一个来自cpluscplus的例子:程序员
// tuple example #include <iostream> // std::cout #include <tuple> // std::tuple, std::get, std::tie, std::ignore int main () { std::tuple<int,char> foo (10,'x'); auto bar = std::make_tuple ("test", 3.1, 14, 'y'); std::get<2>(bar) = 100; // access element int myint; char mychar; std::tie (myint, mychar) = foo; // unpack elements std::tie (std::ignore, std::ignore, myint, mychar) = bar; // unpack (with ignore) mychar = std::get<3>(bar); std::get<0>(foo) = std::get<2>(bar); std::get<1>(foo) = mychar; std::cout << "foo contains: "; std::cout << std::get<0>(foo) << ' '; std::cout << std::get<1>(foo) << '\n'; return 0; }
上述代码中std::tie
就至关于将tuple
中的元素拿出来放入指定的元素中。具体实现来看一下tuple
的简版源码:编程
template<typename... Values> class tuple; template<> class tuple<> {}; template<typename Head, typename... Tail> class tuple<Head, Tail...>:private tuple<Tail...> { typedef tuple<Tail...> inherited; public: tuple(){} tuple(Head v, Tail... vtail):m_head(v),inherited(vtail...){} typename Head::type head(){ return m_head; } inherited& tail() { return *this; } protected: Head m_head; };
这里有一个关键点就在于,tuple
继承自去掉第一个参数后的类,这里模板会为咱们自动生成全部的继承关系。举个简单的例子:函数
tuple<int, float, string> t(41, 6.3, "nico");
这样的语句会生成以下的继承关系:ui
Type Traits就是"类型的特征"的意思。在C++元编程中,程序员很多时候都须要了解一些类型的特征信息,并根据这些类型信息选择应有的操做。好比:this
#include <type_traits> template<typename T> constexpr bool is_pod(T) { return std::is_pod<T>::value; }
这里就定义了一个名为is_pod的函数模板。该函数模板只是type_traits中模板类is_pod的简单包装。经过该函数,咱们能够判断一个类型的数据是否为POD类型的:设计
int main(){ int a; std::cout << is_pod(a) << std::endl; // 1 }
值得注意的是,Type Traits是用于元编程中的元素,并且咱们的函数表达式使用了constexpr进行修饰,那么这就意味着程序员能够在编译时期就得到is_pod返回的值。在本例中,咱们得到的值为true(1)。C++ 11标准提供了各式各样的Type Traits。code
这里咱们只摘取了一部分。在最初的设计中,C++语言设计者为的Type Traits进行了简单的分类。不过在后来的语言标准中,Type Traits也几经修正演化,也不是由一个文档可以观察全貌的。所幸的是上面的连接应该提供了最新的Type Traits的内容。对象
除去判断类型的特性,
而从实现上讲,这些Type Traits一般是经过模板特化的元编程手段来完成的,好比在g++ 4.8.1的
/// is_const template<typename> struct is_const : public false_type { }; // 版本 1 template<typename _Tp> struct is_const<_Tp const> : public true_type { }; // 版本 2
这里的false_type和true_type则是两个helper class,其定义以下:
/// integral_constant template<typename _Tp, _Tp __v> struct integral_constant { static constexpr _Tp value = __v; typedef _Tp value_type; typedef integral_constant<_Tp, __v> type; constexpr operator value_type() { return value; } }; template<typename _Tp, _Tp __v> constexpr _Tp integral_constant<_Tp, __v>::value; /// The type used as a compile-time boolean with true value. typedef integral_constant<bool, true> true_type; /// The type used as a compile-time boolean with false value. typedef integral_constant<bool, false> false_type;
若是不想细看代码,也能够简单地说,true_type和false_type就是包含一个静态类成员value的类模板,其静态成员一个为true,一个为false,仅此而已。这样一来,经过特化,若是咱们使用const类型做为模板is_const类型参数,则能够得到其常量静态成员value的值为true(1)。这是由于模板在实例化的时候选择了"版本2"。反过来,若是模板实例化到"版本1",则value常量静态成员为false(0)。以下例所示:
#include <iostream> #include <type_traits> int main(){ int a; const int b = 3; std::cout << std::is_const<decltype(a)>::value << std::endl; // 0 std::cout << std::is_const<decltype(b)>::value << std::endl; // 1 }
此外,还有一点值得指出,并不是全部的Type Traits都可以使用上面的元编程的手段来实现。C++语言设计者在实践中进行了一些考量,让部分的Type Traits实现为了intrinsic,简单地说,就是要编译器辅助来计算出其值。咱们能够看看g++4.8.1中POD的定义:
/// is_pod // Could use is_standard_layout && is_trivial instead of the builtin. template<typename _Tp> struct is_pod : public integral_constant<bool, __is_pod(_Tp)> { };
这里的__is_pod就是编译器内部的intrinsic。事实上,在C++11中,编译器必须辅助实现不少Type Traits的模板类。总的来讲,Type Traits就是经过元编程的手段,以及编译器的辅助来实现的。