通常在產生對象的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就是經過元編程的手段,以及編譯器的輔助來實現的。