• STL源码剖析—stl_config


    操作系统:centos 6.4
    STL源码版本:3.3

    前言:
        要看一个项目的源码,首先要选中切入点。
        那么在sgi stl 标准库中,其切入点是什么呢?
        答案是:stl_config.h 文件。

        不同的编译器对C++语言的支持程度不尽相同。为了具备广泛移植能力,SGI STL  定义了一个环境组态文件<stl_config.h>。
    其中声明了许多宏定义,在预编译的时候,通过这些宏定义来编译出对于平台的程序。


    1,stl_config.h在linux平台下的实现:
        
        1.1)为了知道linux平台下sgi stl 的宏定义有那些是被定义了的,有个简单,直接的方法,直接输出其宏名字。
            测试代码如下:

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    
    
    void    test(void)
    {
    #ifdef _PTHREADS
        cout<<"define __STL_PTHREADS"<<endl;
    #endif
    
    # if defined(__sgi) && !defined(__GNUC__)
        cout<<"__sgi begin"<<endl;
    # if !defined(_BOOL)
            cout<<"__STL_NEED_BOOL"<<endl;
    # endif
    # if !defined(_TYPENAME_IS_KEYWORD)
             cout<<"__STL_NEED_TYPENAME"<<endl;
    # endif
    # ifdef _PARTIAL_SPECIALIZATION_OF_CLASS_TEMPLATES
            cout<<"__STL_CLASS_PARTIAL_SPECIALIZATION"<<endl;
    # endif
    # ifdef _MEMBER_TEMPLATES
            cout<<"__STL_MEMBER_TEMPLATES"<<endl;
    # endif
    # if !defined(_EXPLICIT_IS_KEYWORD)
            cout<<"__STL_NEED_EXPLICIT"<<endl;
    # endif
    # ifdef __EXCEPTIONS
            cout<<"__STL_USE_EXCEPTIONS"<<endl;
    # endif
    # if (_COMPILER_VERSION >= 721) && defined(_NAMESPACES)
            cout<<"__STL_USE_NAMESPACES"<<endl;
    # endif 
    # if !defined(_NOTHREADS) && !defined(__STL_PTHREADS)
            cout<<"__STL_SGI_THREADS"<<endl;
    # endif
        cout<<"__sgi end"<<endl<<endl;
    # endif
    
    # ifdef __GNUC__
        cout<<"__GNUC__ begin"<<endl;
    # include <_G_config.h>
    # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
            cout<<"__STL_STATIC_TEMPLATE_MEMBER_BUG"<<endl;
            cout<<"__STL_NEED_TYPENAME"<<endl;
            cout<<"__STL_NEED_EXPLICIT"<<endl;
    # else
            cout<<"__STL_CLASS_PARTIAL_SPECIALIZATION"<<endl;
            cout<<"__STL_FUNCTION_TMPL_PARTIAL_ORDER"<<endl;
            cout<<"__STL_EXPLICIT_FUNCTION_TMPL_ARGS"<<endl;
            cout<<"__STL_MEMBER_TEMPLATES"<<endl;
    # endif
        /* glibc pre 2.0 is very buggy. We have to disable thread for it.
           It should be upgraded to glibc 2.0 or later. */
    # if !defined(_NOTHREADS) && __GLIBC__ >= 2 && defined(_G_USING_THUNKS)
            cout<<"__STL_PTHREADS"<<endl;
    # endif
    # ifdef __EXCEPTIONS
            cout<<"__STL_USE_EXCEPTIONS"<<endl;
    # endif
        cout<<"__GNUC__ end"<<endl<<endl;
    # endif
    
    # if defined(__SUNPRO_CC) 
        cout<<"__SUNPRO_CC begin"<<endl;
        cout<<"__STL_NEED_BOOL"<<endl;
        cout<<"__STL_NEED_TYPENAME"<<endl;
        cout<<"__STL_NEED_EXPLICIT"<<endl;
        cout<<"__STL_USE_EXCEPTIONS"<<endl;
        cout<<"__SUNPRO_CC end"<<endl<<endl;
    # endif
    
    # if defined(__COMO__)
        cout<<"__COMO__ begin"<<endl;
        cout<<"__STL_MEMBER_TEMPLATES"<<endl;
        cout<<"__STL_CLASS_PARTIAL_SPECIALIZATION"<<endl;
        cout<<"__STL_USE_EXCEPTIONS"<<endl;
        cout<<"__STL_USE_NAMESPACES"<<endl;
        cout<<"__COMO__ end"<<endl<<endl;
    # endif
    
    # if defined(_MSC_VER)
    cout<<"_MSC_VER begin"<<endl;
    # if _MSC_VER > 1000
            cout<<"include <yvals.h>"<<endl;
    # else
            cout<<"__STL_NEED_BOOL"<<endl;
    # endif
            cout<<"__STL_NO_DRAND48"<<endl;
            cout<<"__STL_NEED_TYPENAME"<<endl;
    # if _MSC_VER < 1100
            cout<<"__STL_NEED_EXPLICIT"<<endl;
    # endif
            cout<<"__STL_NON_TYPE_TMPL_PARAM_BUG"<<endl;
            cout<<"__SGI_STL_NO_ARROW_OPERATOR"<<endl;
    # ifdef _CPPUNWIND
            cout<<"__STL_USE_EXCEPTIONS"<<endl;
    # endif
    # ifdef _MT
            cout<<"__STL_WIN32THREADS"<<endl;
    # endif
        cout<<"_MSC_VER end"<<endl<<endl;
    # endif
    
    # if defined(__BORLANDC__)
        cout<<"__BORLANDC__ begin"<<endl;
        cout<<"__STL_NO_DRAND48"<<endl;
        cout<<"__STL_NEED_TYPENAME"<<endl;
        cout<<"__STL_LIMITED_DEFAULT_TEMPLATES"<<endl;
        cout<<"__SGI_STL_NO_ARROW_OPERATOR"<<endl;
        cout<<"__STL_NON_TYPE_TMPL_PARAM_BUG"<<endl;
    # ifdef _CPPUNWIND
            cout<<"__STL_USE_EXCEPTIONS"<<endl;
    # endif
    # ifdef __MT__
            cout<<"__STL_WIN32THREADS"<<endl;
    # endif
        cout<<"__BORLANDC__ end"<<endl<<endl;
    # endif
    
    
    # if defined(__STL_NEED_BOOL)
        cout<<"__STL_NEED_BOOL begin"<<endl;
        cout<<"typedef int bool;"<<endl;
        cout<<"define true 1"<<endl;
        cout<<"define false 0"<<endl;
        cout<<"__STL_NEED_BOOL end"<<endl<<endl;
    # endif
    
    # ifdef __STL_NEED_TYPENAME
        cout<<"define typename"<<endl;
    # endif
    
    # ifdef __STL_NEED_EXPLICIT
        cout<<"define explicit"<<endl;
    # endif
    
    # ifdef __STL_EXPLICIT_FUNCTION_TMPL_ARGS
        cout<<"__STL_NULL_TMPL_ARGS <>"<<endl;
    # else
        cout<<"__STL_NULL_TMPL_ARGS"<<endl;
    # endif
    
    # ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
        cout<<"__STL_TEMPLATE_NULL template<>"<<endl;
    # else
        cout<<"__STL_TEMPLATE_NULL"<<endl;
    # endif
    
    // __STL_NO_NAMESPACES is a hook so that users can disable namespaces
    // without having to edit library headers.
    # if defined(__STL_USE_NAMESPACES) && !defined(__STL_NO_NAMESPACES)
        cout<<"__STL_USE_NAMESPACES begin"<<endl;
        cout<<"__STD std"<<endl;
        cout<<"__STL_BEGIN_NAMESPACE namespace std {"<<endl;
        cout<<"__STL_END_NAMESPACE }"<<endl;
        cout<<"__STL_USE_NAMESPACE_FOR_RELOPS"<<endl;
        cout<<"__STL_BEGIN_RELOPS_NAMESPACE namespace std {"<<endl;
        cout<<"__STL_END_RELOPS_NAMESPACE }"<<endl;
        cout<<"__STD_RELOPS std"<<endl;
        cout<<"__STL_USE_NAMESPACES end"<<endl<<endl;
    # else
        cout<<"! __STL_USE_NAMESPACES begin"<<endl;
        cout<<"__STD "<<endl;
        cout<<"__STL_BEGIN_NAMESPACE "<<endl;
        cout<<"__STL_END_NAMESPACE "<<endl;
        cout<<"__STL_USE_NAMESPACE_FOR_RELOPS"<<endl;
        cout<<"__STL_BEGIN_RELOPS_NAMESPACE "<<endl;
        cout<<"__STL_END_RELOPS_NAMESPACE "<<endl;
        cout<<"__STD_RELOPS "<<endl;
        cout<<"! __STL_USE_NAMESPACES end"<<endl<<endl;
    # endif
    
    # ifdef __STL_USE_EXCEPTIONS
        cout<<"__STL_USE_EXCEPTIONS begin"<<endl;
        cout<<"__STL_TRY try"<<endl;
        cout<<"__STL_CATCH_ALL catch(...)"<<endl;
        cout<<"__STL_RETHROW throw"<<endl;
        cout<<"__STL_NOTHROW throw()"<<endl;
        cout<<"__STL_UNWIND(action) catch(...) { action; throw; }"<<endl;
        cout<<"__STL_USE_EXCEPTIONS end"<<endl<<endl;
    # else
        cout<<"! __STL_USE_EXCEPTIONS begin"<<endl;
        cout<<"__STL_TRY "<<endl;
        cout<<"__STL_CATCH_ALL if (false)"<<endl;
        cout<<"__STL_RETHROW "<<endl;
        cout<<"__STL_NOTHROW "<<endl;
        cout<<"__STL_UNWIND(action) "<<endl;
        cout<<"! __STL_USE_EXCEPTIONS end"<<endl<<endl;
    # endif
    
    #ifdef __STL_ASSERTIONS
    # include <stdio.h>
        cout<<"__stl_assert(expr) 
        if (!(expr)) { fprintf(stderr, "%s:%d STL assertion failure: %s
    ", 
                 __FILE__, __LINE__, # expr); abort(); }"<<endl;
    #else
        cout<<"__stl_assert(expr)"<<endl;
    #endif
    
    
    }
    
    int main(void)
    {
        test();
    
        return 0;
    }

    运行结果:

    [root@localhost stlsource]# g++ -o lconfig1 lconfig1.cpp 
    [root@localhost stlsource]# ./lconfig1 
    __GNUC__ begin
    __STL_CLASS_PARTIAL_SPECIALIZATION
    __STL_FUNCTION_TMPL_PARTIAL_ORDER
    __STL_EXPLICIT_FUNCTION_TMPL_ARGS
    __STL_MEMBER_TEMPLATES
    __STL_PTHREADS
    __STL_USE_EXCEPTIONS
    __GNUC__ end
    
    __STL_NULL_TMPL_ARGS
    __STL_TEMPLATE_NULL
    ! __STL_USE_NAMESPACES begin
    __STD 
    __STL_BEGIN_NAMESPACE 
    __STL_END_NAMESPACE 
    __STL_USE_NAMESPACE_FOR_RELOPS
    __STL_BEGIN_RELOPS_NAMESPACE 
    __STL_END_RELOPS_NAMESPACE 
    __STD_RELOPS 
    ! __STL_USE_NAMESPACES end
    
    ! __STL_USE_EXCEPTIONS begin
    __STL_TRY 
    __STL_CATCH_ALL if (false)
    __STL_RETHROW 
    __STL_NOTHROW 
    __STL_UNWIND(action) 
    ! __STL_USE_EXCEPTIONS end
    
    __stl_assert(expr)

       1.2)通过以上的测试,可以得到在linux平台下,stl_config.h 的实现定义如下:
            为了缩小篇幅,把相关的版权信息的屏蔽了。

    #ifndef __STL_CONFIG_H
    # define __STL_CONFIG_H
    
    
    # ifdef __GNUC__
    # include <_G_config.h>
    # define __STL_CLASS_PARTIAL_SPECIALIZATION
    # define __STL_FUNCTION_TMPL_PARTIAL_ORDER
    # define __STL_EXPLICIT_FUNCTION_TMPL_ARGS
    # define __STL_MEMBER_TEMPLATES
    # define __STL_PTHREADS
    # define __STL_USE_EXCEPTIONS
    # endif
    
    
    # define __STL_NULL_TMPL_ARGS
    # define __STL_TEMPLATE_NULL
    
    
    # define __STD 
    # define __STL_BEGIN_NAMESPACE 
    # define __STL_END_NAMESPACE 
    # undef __STL_USE_NAMESPACE_FOR_RELOPS
    # define __STL_BEGIN_RELOPS_NAMESPACE 
    # define __STL_END_RELOPS_NAMESPACE 
    # define __STD_RELOPS 
    
    
    # define __STL_TRY 
    # define __STL_CATCH_ALL if (false)
    # define __STL_RETHROW 
    # define __STL_NOTHROW 
    # define __STL_UNWIND(action) 
    
    
    # define __stl_assert(expr)
    
    #endif /* __STL_CONFIG_H */
    
    // Local Variables:
    // mode:C++
    // End:

    2,对stl_config.h中的宏的详解
        2.1)__STL_STATIC_TEMPLATE_MEMBER_BUG 

      1. 如果编译器不支持static members of template classes(模板类静态成员),  
      2. //       则定义__STL_STATIC_TEMPLATE_MEMBER_BUG 
    #include<iostream>
    using namespace std;
    
    template <typename T>
    class testClass {
    public:
        static int _data;
    };
    //只对成员实现特化,记得加上template<>
    template<>
    int testClass<int>::_data=1;
    
    template<>
    int testClass<char>::_data=2;
    
    int main()
    {
        cout<<testClass<int>::_data<<endl;
        cout<<testClass<char>::_data<<endl;
    
        testClass<int> Obji1,Obji2;
        testClass<char> Objc1,Objc2;
    
        cout<<Obji1._data<<endl;
        cout<<Obji2._data<<endl;
        cout<<Objc1._data<<endl;
        cout<<Objc2._data<<endl;
    
        Obji1._data=3;
        Objc1._data=4;
    
        cout<<Obji1._data<<endl;
        cout<<Obji2._data<<endl;
        cout<<Objc1._data<<endl;
        cout<<Objc2._data<<endl;
    }

    运行结果:

    [root@localhost stlsource]# ./lconfig3 
    1
    2
    1
    1
    2
    2
    3
    3
    4
    4

       2.2)__STL_CLASS_PARTIAL_SPECIALIZATION

      1. 如果编译器支持partial specialization of class templates(局部特殊化的类模板),  
      2. //       则定义__STL_CLASS_PARTIAL_SPECIALIZATION  
      3. //       参考文献: http://msdn.microsoft.com/en-us/library/9w7t3kf1(v=VS.71).aspx 
    #include<iostream>
    using namespace std;
    
    template <class I,class O>
    struct testClass
    {
        testClass() {cout<<"I,O"<<endl;}
    };
    //对类实现偏特化
    template <class T>
    struct testClass<T*,T*>
    {
        testClass() {cout<<"T*,T*"<<endl;}
    };
    //对类实现偏特化
    template<class T>
    struct testClass<const T*,T*>
    {
        testClass(){ cout<<"const T*,T*"<<endl;}
    };
    
    int main()
    {
        testClass<int,char> obj1;
        testClass<int*,int*> obj2;
        testClass<const int*,int*> obj3;
    }

    运行结果:

    [root@localhost stlsource]# ./lconfig5
    I,O
    T*,T*
    const T*,T*

        2.3)__STL_FUNCTION_TMPL_PARTIAL_ORDER

      1. 如果编译器支持partial ordering of function templates(部分排序函数模板),  
      2. //       则定义__STL_FUNCTION_TMPL_PARTIAL_ORDER  
      3. //       参考资料: http://msdn.microsoft.com/zh-cn/library/zaycz069.aspx 
    #include<iostream>
    using namespace std;
    
    class alloc{
    };
    
    template <class T,class Alloc=alloc>
    class vector{
    public:
        void swap(vector<T,Alloc>&) {cout<<"swap()"<<endl;}
    };
    //本来是#ifdef__STL_FUNCION_TMPL_PARTIAL_ORDER,但是貌似不支持
    #ifndef __STL_FUNCION_TMPL_PARTIAL_ORDER
    template <class T,class Alloc>
    inline void swap(vector<T,Alloc>& x,vector<T,Alloc>& y)
    {
        x.swap(y);
    }
    #endif // __STL_FUNCION_TMPL_PARTIAL_ORDER
    
    int main()
    {
        vector<int>x,y;
        swap(x,y);
    }

    运行结果:

    [root@localhost stlsource]# ./lconfig6 
    swap()

        2.4)__STL_EXPLICIT_FUNCTION_TMPL_ARGS 
            整个 SGI STL  内都没有用到此一常数定义

      1. 如果编译器支持calling a function template by providing its template  
      2. //       arguments explicitly(显式指定调用模板函数的模板参数) 

        2.5)__STL_MEMBER_TEMPLATES 

    1. 如果编译器支持template members of classes(类模板成员),  
    2. //       则定义__STL_MEMBER_TEMPLATES 
    #include<iostream>
    #include<typeinfo>
    using namespace std;
    
    class alloc{
    };
    //类模板
    template <typename T,typename Alloc=alloc>
    class vector{
    public:
        typedef T value_type;
        typedef value_type* iterator;
    //函数模板
        template <typename I>
        void insert(iterator position,I first,I last)
        {
            cout<<"insert()"<<endl;
            cout<<typeid(I).name()<<endl;
        }
    };
    
    int main()
    {
        int ia[5]={0,1,2,3,4};
        vector<int> x;
        vector<int>::iterator ite;
        x.insert(ite,*ia,*(ia+5));
    }

    运行结果:

    [root@localhost stlsource]# ./lconfig8
    insert()
    i

        2.6)__STL_LIMITED_DEFAULT_TEMPLATES

      1. 如果编译器不能根据前一个模板参数设定后面的默认模板参数,  
      2. //       则定义__STL_LIMITED_DEFAULT_TEMPLATES
    #include<iostream>
    #include<cstddef>
    using namespace std;
    
    class alloc{};
    
    template <typename T,class Alloc=alloc,size_t Bufsiz=0>
    class deque{
    public:
        deque(){cout<<"deque"<<endl;}
    };
    
    template <typename T,class Sequence=deque<T> >
    class stack{
    public:
        stack(){ cout<<"stack"<<endl;}
    private:
        Sequence c;//调用deque的构造函数初始化
    };
    
    int main()
    {
        stack<int> x;
    }
    

      运行结果:

    [root@localhost stlsource]# ./lconfig10 
    deque
    stack
    

          2.7)__STL_NON_TYPE_TMPL_PARAM_BUG 

      1. 如果编译器处理模板函数的non-type模板参数类型推断有困难,  
      2. //       则定义__STL_NON_TYPE_TMPL_PARAM_BUG

     

    #include<iostream>
    #include<cstddef>
    using namespace std;
    
    class alloc{};
    
    inline size_t __deque_buf_size(size_t n,size_t sz)
    {
        return n!=0?n:(sz<512?size_t(512/sz):size_t(1));
    }
    
    template <class T,class Ref,class Ptr,size_t Bufsiz>
    struct __deque_iterator{
        typedef __deque_iterator<T,T&,T*,Bufsiz> iterator;
        typedef __deque_iterator<T,const T&,const T*,Bufsiz> const_iterator;
        static size_t buffer_size() {return __deque_buf_size(Bufsiz,sizeof(T));}
    };
    
    template <class T,class Alloc=alloc,size_t Bufsiz=0>
    class deque
    {
    public:
        typedef __deque_iterator<T,T&,T*,Bufsiz> iterator;
    };
    
    int main()
    {
        cout<<deque<int>::iterator::buffer_size()<<endl;
        cout<<deque<int,alloc,64>::iterator::buffer_size()<<endl;
    }
    

      运行结果:

      

         2.8)__STL_NULL_TMPL_ARGS(bound friend template friend)

    <stl_config.h>定义__STL_NULL_TMPL_ARGS如下:

    #ifdef __STL_NULL_TMPL_ARGS
    # define __STL_NULL_TMPL_ARGS <>
    #else
    # define __STL_NULL_TMPL_ARGS
    #endif
    

      这个组态常量常常出现在类似这样的场合(class template的friend函数声明)。

    // in <stl_stack.h>
    template<class T,class Sequence=deque<T> >
    class stack{
        friend bool operator==___STL_NULL_TMPL_ARGS(const stack &,const stack&);
        friend bool operator< __STL_NULL_TMPL_ARGS(const stack&,const stack&);
        ...
    };
    
    展开后就变成了:
    
    template<class T,class Sequence=deque<T> >
    class stack{
        friend bool operator== <>(const stack &,const stack&);
        friend bool operator<  <>(const stack&,const stack&);
        ...
    };
    

      这种奇特的语法是为了实现所谓的 bound friend templates,也就是说class template的某个具现体(instantiation)与其friend function template的某个具现体有一对一的关系。下面是测试程序:

    #include<iostream>
    #include<cstddef>
    using namespace std;
    
    class alloc{};
    
    template<class T,class Alloc=alloc,size_t BufSiz=0>
    class deque
    {
    public:
        deque(){cout<<"deque"<<endl;}
    };
    //类模板与友元的一对一关系需要前置声明
    template<class T,class Sequence>
    class stack;
    
    template<class T,class Sequence>
    bool operator==(const stack<T,Sequence>& x,const stack<T,Sequence>& y);
    
    template<class T,class Sequence>
    bool operator<(const stack<T,Sequence>& x,const stack<T,Sequence>& y);
    
    template<class T,class Sequence=deque<T> >
    class stack
    {
        //friend bool operator==<T>(const stack<T>&,const stack<T>&);
        //friend bool operator< <T>(const stack<T>&,const stack<T>&);
        //下面的都是等价于上面的
        //friend bool operator== <T>(const stack&,const stack&);
        //friend bool operator< <T>(const stack&,const stack&);
    
        friend bool operator== <>(const stack&,const stack&);
        friend bool operator< <>(const stack&,const stack&);
    public:
        stack(){cout<<"stack"<<endl;}
    private:
        Sequence c;
    };
    
    template<class T,class Sequence>
    bool operator==(const stack<T,Sequence> &x,const stack<T,Sequence> &y)
    {
        return cout<<"operator=="<<'	';
    }
    
    template<class T,class Sequence>
    bool operator<(const stack<T,Sequence> &x,const stack<T,Sequence> &y)
    {
        return cout<<"operator<"<<'	';
    }
    
    int main()
    {
        stack<int> x;
        stack<int> y;
        cout<<(x==y)<<endl;
        cout<<(x<y)<<endl;
    
        stack<char> y1;
    //    cout<<(x==y1)<<endl;
     //   cout<<(x<y1)<<endl;
    }
    

      运行结果:

        2.9)__STL_TEMPLATE_NULL(class template explicit specialization)


    <stl_config.h>定义了一个__STL_TEMPLATE_NULL如下:

    #ifdef __STL_CLASS_PAPTIAL_SPECIALIZATION
    #define __STL_TEMPLATE_NULL template<>
    #else
    #define __STL_TEMPLATE_NULL
    #endif
    

      这个组态常量常常出现在类似这样的场合:

    //in <type_traits.h>
    template <class type> struct  _type_traits {...};
    __STL_TEMPLATE_NULL struct _type_traits<char> {};
    
    //in <stl_hash_fun.h>
    template <class key> struct hash{};
    __STL_TEMPLATE_NULL struct hash<char> {};
    __STL_TEMPLATE_NULL struct hash<unsigned char> {};
    

      展开后:

    //in <type_traits.h>
    template <class type> struct  _type_traits {...};
    template<> struct _type_traits<char> {};
    
    //in <stl_hash_fun.h>
    template <class key> struct hash{};
    template<> struct hash<char> {};
    template<> struct hash<unsigned char> {};
    

      这就是所谓的class template explicit specialization。

    下面是一个测试程序:

    #include<iostream>
    //不能使用 using namespace std 这会将标准库中的hash引入,然后通不过
    using std::cout;
    using std::endl;
    
    //貌似我使用g++编译,定义为空不能通过
    //#define __STL_TEMPLATE_NULL
    #define __STL_TEMPLATE_NULL template<>
    
    template <class key>
    struct hash
    {
        //重载函数调用运算符
        void operator()()
        {
            cout<<"hash<T>"<<endl;
        }
    };
    
    template<>
    struct hash<char>
    {
        void operator()()
        {
            cout<<"hash<char>"<<endl;
        }
    };
    
    __STL_TEMPLATE_NULL
    struct hash<unsigned char>
    {
        void operator()()
        {
            cout<<"hash<unsigned char"<<endl;
        }
    };
    
    int main()
    {
        hash<long> t1;
        hash<char> t2;
        hash<unsigned char> t3;
    
        t1();//函数对象和函数指针类似,可以作为可调用对象
        t2();
        t3();
    }
    

      运行结果:

  • 相关阅读:
    修改spring boot 启动logo
    查看jvm常用命令
    intellij IDEA破解
    hdu 新生晚会
    How many prime numbers(素数)
    2077 汉诺塔IV
    Factorial
    双人黑白块
    EasyX
    七夕情人节
  • 原文地址:https://www.cnblogs.com/wuchanming/p/4059602.html
Copyright © 2020-2023  润新知