[Boolan] C++第十週 STL 泛型編程(四)

1. 一個萬用的Hash Function

參考鏈接:Parameter pack:http://en.cppreference.com/w/cpp/language/parameter_pack

省略號和可變參數模板: https://msdn.microsoft.com/zh-cn/library/dn439779.aspx

解析:下面代碼中,hash_val就是自定義的hash_function,是使用了函數模版的函數重載,
     並且使用了可變參數模版,這是C++11之後纔有的特性,
     本質上是通過編譯器自動推導函數模版的實參這一功能還推動函數進行hash的動力

    首先先說一下用來具體求hash值的函數,hash_combine,這個函數並沒有使用什麼新特性,
    就只是根據經驗值求出了val這個參數的hash值,其中的計算大多根據經驗值得到,只是爲了求出來的hash值儘量不重複,並沒有什麼算法的根據

    之後說一下用來分解參數的hash_val,他一共有三個函數重載,分別是
        hash_val(const Types&... args);        //所有參數,最外層的包裹函數
        hash_val(size_t& seed, const T&val, const Types& ...args);    //通過函數參數,把所有的函數參數分解成 第一個,和其他
        hash_val(size_t &seed, const Tval)         //計算單一參數的hash值,並且合併進seed中

    通過上邊的註釋,可以清晰的得知這之間的調用關係,以下列代碼中hash_val(100,100,300) 這次調用來說,
    首先編譯器會根據函數參數的類型,自動調用hash_val(const Types&...  args);
    然後這是一個seed的初始值,然會在調用hash_val(size_t& seed, const T&val, const Types& ...args) 來遞歸的進行各個參數的hash值計算,
        並且通過引用的方式把seed進行傳遞和獲取,直至計算完最後一個參數的hash值,然後把seed返回

    下面還有把hash_val封裝成一個仿函數,用於計算自定義類型的hash值,還有讓容器unordered_set使用我們的hash算法
#include <iostream>
#include <unordered_set>

using namespace std;

//------------------start
template <typename T>
inline void hash_combine(size_t &seed, const T &val)
{
    seed ^= hash<T>()(val) + 0x9e3779b9 + (seed<<6) + (seed >>2);
}

template <typename... Types>
inline size_t hash_val(const Types&... args)
{
    size_t seed = 0;
    hash_val(seed, args...);
    return seed;
}

template <typename T, typename... Types>
inline void hash_val(size_t& seed, const T&val, const Types&... args)
{
    hash_combine(seed, val);
    hash_val(seed, args...);
}


template <typename T>
inline void hash_val(size_t &seed, const T&val)
{
    hash_combine(seed, val);
}

//-------------------end


struct Customer{
    string name;
    int no;
    Customer(string name, int no)
    :name(name), no(no)
    {}


};

bool operator == (const Customer &self, const Customer &oter)
{
    if(self.name == oter.name && self.no == oter.no)
    {
        return true;
    }
    return false;
}

class CustomerHash {
public:
    size_t operator()(const Customer &c) const
    {
        return hash_val(c.name, c.no);
    }

};

int main(int argc, char *argv[])
{

    std::cout << "100's hash code is " << hash_val(100,100,300) <<endl;

    std::cout << CustomerHash()(Customer("Albin", 24)) << endl;

    unordered_set<Customer, CustomerHash> custset;

    custset.insert(Customer("Alice", 20));

    return 0;
} 

2. Tuple

tuple 簡單用例
#include <iostream>
#include <string>
#include <tuple>

using namespace std;

int main(int argc, char *argv[])
{
    tuple<int, float, string> t1(41, 6.3, "nice");

    cout << "tuple<int, float, string>, sizeof = " << sizeof(t1) <<endl;

    cout << get<0>(t1) << endl;
    cout << get<1>(t1) << endl;
    cout << get<2>(t1) << endl;

    typedef tuple<int, float, string> TupleType;
    cout << "sizeof = " << tuple_size<TupleType>::value << endl;

    return 0;
}
大體的實現邏輯如下:
    主要是使用的模版繼承來實現的,。。模版的繼承。。。 
    首先是因爲在編譯器編譯之前,tuple 有多少成員構就已經確定了,所以可以利用編譯器的模版的自動推導來實現,自動推導父類。。。

    以 myTuple<int, float, string> 爲例,
        首先編譯器自動匹配到的是 myTuple<Head, Tail> 的偏特化,所以就分成了兩部分 myTuple<int> 和 myTuple<float,string>
        並且 myTuple<int> 的父類是 myTuple<float,string>
        然後 myTuple<float,string> 又匹配到 myTuple<Head, Tail> 的偏特化, 並且兩部分是 myTuple<float> 和 myTuple<string>
        我感覺這就是一個模版的遞歸繼承,作爲一個遞歸,最重要的就是怎麼停止
        最後一個就是 myTuple<string>, 實際上這個又分爲了  myTuple<string> 和 myTuple<>,  並且template<> 是一個偏特化


        還有一點就是他還利用繼承的類型轉換獲取到父類的m_head,   
        inherited就是父類的重命名,然後通過tail獲取到父類的對象, 這個一級一級的上去  可以獲得每一個類型成員的data
#include <iostream>
#include <string>

using namespace std;

template<typename... Value> class myTuple;
template<> class myTuple<> {};

template<typename Head, typename... Tail>
class myTuple<Head, Tail...>
:private myTuple<Tail...>
{
    typedef myTuple<Tail...> inherited;

public:
    myTuple(){}
    myTuple(Head v, Tail... vtail)
    : m_head(v), inherited(vtail...)
    {
    }

    Head head() {return m_head;}

    inherited& tail() {return *this;}
protected:
    Head m_head;
};


int main(int argc, char *argv[])
{
    myTuple<int, float, string> t1(10, 6.5, "Albin");

    cout << t1.head() << endl;
    cout << t1.tail().head() << endl;
    cout << t1.tail().tail().head() << endl;

    return 0;
}

3. type traits

參考鏈接:http://www.cnblogs.com/pugang/archive/2012/10/17/2727378.html
這個和之前的 iterator tarits 差不多,只不過這個是用在類型識別上面的

下面說一下 is_void 的大體實現,據說這個還是實現最簡單的
    利用模版的偏特化把各個修飾部分去掉,並且還是利用偏特化來判斷你想要知道的

template <typename _Tp>
struct remove_const {
    typedef _Tp    type;
};
template <typename _Tp>
struct remove_cosnt <_Tp const> {
    typedef _Tp    type;
};

template <typename _Tp> 
struct remove_volatile {
    typedef _Tp    type;
};
template <typename _Tp>
struct temove_volatile<_Tp volatile> {
    typedef _Tp    type;
};

template<typename _Tp>        //合併上面倆個
struct remove_cv {
    typedef typename 
        remove_const<typename remove_volatile<_Tp>::type>::type     type;
};

template<typename _Tp>
struct add_const {
    typedef _Tp   const  type;
};


template<typename>
struct __is_void_helper : public false_type {};

template<>
struct __is_void_helper<void> : public true_type {};

template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type 
{};

template<typename _Tp>
struct false_type {
    static bool type = false;
};

template<typename _Tp>
struct ture_type {
    static bool type = true;
};

moveable

moveable是爲了應對那種需要使用大量拷貝動作,然後原來的對象直接析構掉的場景,尤其是帶指針的深拷貝,此時就顯得有些浪費了,
moveable是淺拷貝,最顯著的案例就是vector<string>在擴展內存的時候,會重新申請新的內存塊,把原有的內容拷貝過去,然後釋放原來的內存,
這個時候使用moveable就不需要重複申請內存來進行string的深拷貝了,速度的提升是非常顯著的

下面是簡單的使用案例


#include <stdio.h>
#include <iostream>

using namespace std;

class MyString {
public:
    MyString()
    :_data(NULL), _len(0)
    {}

    MyString(const char *p)
    : _len(strlen(p)) {
        _init_data(p);
    }

    MyString(const MyString&str)
    :_len(str._len) {
        _init_data(str._data);
    }

    MyString(MyString && str) noexcept      //
    : _data(str._data), _len(str._len) {
        str._len = 0;
        str._data = NULL;

        cout << "MyString(MyString && str) noexcept   "<<endl;
    }

    MyString& operator=(const MyString& str) {
        if(this != &str) {
            if(_data) delete _data;
            _len = str._len;
            _init_data(str._data);
        }
        return *this;
    }

    MyString& operator= (MyString&& str) noexcept {
        if(this != &str) {
            if( _data) delete _data;
            _len = str._len;
            _data = str._data;
            str._len = 0;
            str._data = NULL;
        }

        cout << "MyString& operator= (MyString&& str) noexcept"<<endl;
        return *this;
    }

    virtual ~MyString() {
        if(_data) {
            delete _data;
        }
    }
private:
    char *_data;
    size_t  _len;
    void _init_data(const char *s)
    {
        _data = new char[_len+1];
        memcpy(_data, s, _len);
        _data[_len] = '\0';
    }
};

int main(int argc, char*argv[])
{
    MyString c1 = "asdfasdf";
    MyString c2(std::move(c1)); //顯式調用moveable

    MyString c3;
    c3 = std::move(c2);
    return 0;
}

///////////
MyString(MyString && str) noexcept   
MyString& operator= (MyString&& str) noexcept
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章