• STL源码剖析(五)


    一个万用的Hash Function

    一般在产生对象的hash码时,许多人会将对象中各个类型的元素取得hash码后相加得出该元素的hash码。这样做除了简单没有任何依据,根据实际中的应用,会发现这种方法产生相同的hash码可能性很大。所以C++提出了一种产生hash码的方法。

    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) << '
    ';
    
      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");
    

    这样的语句会生成如下的继承关系:

    avatar

    Type Traits就是"类型的特征"的意思。在C++元编程中,程序员不少时候都需要了解一些类型的特征信息,并根据这些类型信息选择应有的操作。比如:

    #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

    这里我们只摘取了一部分。在最初的设计中,C++语言设计者为的Type Traits进行了简单的分类。不过在后来的语言标准中,Type Traits也几经修正演化,也不是由一个文档能够观察全貌的。所幸的是上面的链接应该提供了最新的Type Traits的内容。

    除去判断类型的特性,<type_traits>中我们也可以找到is_same这样的比较两个类型是否相等的类模板,以及enable_if这样的根据bool值选择类型的类模板,读者可以从上面链接中寻找其使用方式。

    而从实现上讲,这些Type Traits通常是通过模板特化的元编程手段来完成的,比如在g++ 4.8.1的<type_traits>头文件中我们可以找到以下代码:

    /// 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就是通过元编程的手段,以及编译器的辅助来实现的。

  • 相关阅读:
    大数据在智慧城市中的应用
    使用Java+NetBeans设计web服务和页面,用Tomcat部署网页
    VS2013环境下配置OSG(3.4.0版本)
    sort函数用于vector向量的排序
    利用eigen库简单实现矩阵功能
    elasticsearch搜索QueryStringQueryBuilder时的一些问题记录
    Hbase中HMaster作用
    国内加速git下载速度
    JAVA中Integer.valueOf, parsetInt() String.valueOf的区别和结果
    JAVA API操作hbase1.4.2
  • 原文地址:https://www.cnblogs.com/joker-wz/p/10310056.html
Copyright © 2020-2023  润新知