STL源碼剖析(五)

一個萬用的Hash Function

通常在產生對象的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

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

avatar

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 Traitscode

這裏咱們只摘取了一部分。在最初的設計中,C++語言設計者爲的Type Traits進行了簡單的分類。不過在後來的語言標準中,Type Traits也幾經修正演化,也不是由一個文檔可以觀察全貌的。所幸的是上面的連接應該提供了最新的Type Traits的內容。對象

除去判斷類型的特性, 中咱們也能夠找到is_same這樣的比較兩個類型是否相等的類模板,以及enable_if這樣的根據bool值選擇類型的類模板,讀者能夠從上面連接中尋找其使用方式。 blog

而從實現上講,這些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就是經過元編程的手段,以及編譯器的輔助來實現的。

相關文章
相關標籤/搜索