[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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值