• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            Traits 技術 --- 模板元編程 (轉)

            Posted on 2012-12-01 19:57 鑫龍 閱讀(708) 評論(0)  編輯 收藏 引用 所屬分類: c++
                  什么是traits,為什么人們把它認為是C++ Generic Programming的重要技術?

                  簡潔地說,traits如此重要,是因為此項技術允許系統在編譯時根據類型作一些決斷,就好像在運行時根據值來做出決斷一樣。更進一步,此技術遵循“另增一個間接層”的諺語,解決了不少軟件工程問題,traits使您能根據其產生的背景(context) 來做出抉擇。這樣最終的代碼就變得清晰易讀,容易維護。如果你正確運用了traits技術,你就能在不付出任何性能和安全代價的同時得到這些好處,或者能夠契合其他解決方案上的需求。
                  先舉個淺顯易懂的例子來說明traits的用法:
            //首先假如有以下一個泛型的迭代器類,其中類型參數 T 為迭代器所指向的類型:
            template <typename T>
            class myIterator
            {

            };
                   那么當使用myIterator時,怎樣才能知道它所指向元素的類型呢?一種解決方案是為這個類加入一個內嵌類型:
            template <typename T>
            class myIterator
            {
            typedef T value_type;

            };
                  當使用myIterator時,可以通過myIterator::value_type來獲得相應的myIterator所指向的類型。下面舉例使用:
            template <typename T>
            typename myIterator<T>::value_type func(myIterator<T> i)
            {

            }
                  這里定義了一個函數func,返回值類型為參數i所指的類型,也就是模板參數T,那么為什么不直接使用模板參數T,而要繞著圈去使用那個value_type呢?所以我們返回來,當修改func函數時,它能夠適應所有類型的迭代器,不是更好嗎?如下所示:
            template <typename I> //這里的I可以是任意類型的迭代器
            typename I::value_type func(I i)
            {

            }
                  現在,任意定義了value_type內嵌類型的迭代器都可以做為func的參數了,并且func的返回值的類型將與相應迭代器所指的元素的類型一致。至此一切問題似乎都已解決,并且似乎并沒有使用任何特殊的技術。
                  然而當考慮到以下情況時,新的問題便顯現出來了:原生指針也完全可以做為迭代器來使用,然而顯然沒有辦法為原生指針添加一個value_type的內嵌類型,如此一來func()函數就不能適用原生指針了,這不能不說是一大缺憾。那么有什么辦法可以解決這個問題呢?此時不禁想到了用Traits萃取類型信息??梢圆恢苯邮褂胢yIterator的value_type,而是通過Traits類來把這個信息提取出來:(不同的類型,可以有不同的提取方式)
            template <typename T>
            class Traits
            {
            typedef typename T::value_type value_type;
            };
                  這樣以后就可以通過Traits<myIterator>::value_type來提取出myIterator中的value_type,于是func函數改寫成:
            template <typename I> //這里的I可以是任意類型的迭代器
            typename Traits<I>::value_type Foo(I i)
            {

            }
                  然而,即使這樣,那個原生指針的問題仍然沒有解決,因為Trait類還是沒辦法獲得原生指針的相關信息。于是不妨將Traits偏特化(partial specialization):(通過特化、重載特化等手段產出不同的提取方式)
            template <typename T>
            class Traits<T*> //注意 這里針對原生指針進行了偏特化
            {
            typedef typename T value_type;
            };
                 通過上面這個Traits的偏特化版本,一個T*類型的指針所指向的元素的類型為T。如此一來,我們func函數就完全可以適用于原生指針了。比如:
            int * p;
            .
            int i = func(p);
                 Traits會自動推導出p所指元素的類型為int,從而func正確返回。
            -----------------------------------------------------------------------------------------------------------------------------------------------------------
                 現在再看一個更加一般的例子——smart pointers。(智能指針)
                 假設你正在設計一個SmartPtr模板類,對于一個smart pointer 來說,它的最大的用處是可以自動管理內存問題,同時在其他方面又像一個常規指針。但是有些C++的Smart pointer實現技術卻非常令人難以理解。這一殘酷的事實帶來了一個重要實踐經驗:你最好盡一切可能一勞永逸,寫出一個出色的、具有工業強度的 smart pointer來滿足你所有的需求。此外,你通常不能修改一個類來適應你的smart pointer,所以你的SmartPtr一定要足夠靈活。
                 有不少類層次使用引用計數(reference counting)以及相應的函數管理對象的生存期。然而,并沒有reference counting的標準實現方法,每一個C++庫的供應商在實現的語法和/或語義上都有所不同。例如,在你的應用程序中有這樣兩個interfaces:
            第一種智能指針--大部分的類實現了RefCounted接口:
            class RefCounted
            {
            public:
            virtual void IncRef() = 0;
            virtual bool DecRef() = 0;
            // if you DecRef() to zero references, the object is destroyed
            // automatically and DecRef() returns true
            virtual ~RefCounted() {}
            };
            第二種智能指針--第三方提供的Widget類使用不同的接口:
            class Widget
            {
            public:
            void AddReference();
            int RemoveReference(); 
            // returns the remaining number of references; it's the client's
            // responsibility to destroy the object

            };
                  不過你并不想維護兩個smart pointer類,你想讓兩種類共享一個SmartPtr。一個基于traits的解決方案把兩種不同的接口用語法和語義上統一的接口包裝起來,建立針對普通類的通用模板,而針對Widget建立一個特殊化版本,如下:
            template <class T>
            class RefCountingTraits
            {
            static void Refer(T* p)
            {
            p->IncRef(); // assume RefCounted interface
            }
            static void Unrefer(T* p)
            {
            p->DecRef(); //assume RefCounted interface
            }
            };
            template<>
            class RefCountingTraits<Widget>
            {
            static void Refer(Widget* p)
            {
            p->AddReference(); //use Widget interface
            }
            static void Unrefer(Widget* p)
            {
            //use Widget interface
            If (p->RemoveReference() == 0)
            delete p;
            }
            };
                  在SmartPtr里,我們像這樣使用RefCountingTraits:
            template <class T>
            class SmartPtr
            {
            private:
            typedef RefCountingTraits<T> RCTraits;
            T* pointee_;
            public:

            ~SmartPtr()
            {
            RCTraits::Unrefer(pointee_);
            }
            };
                  當然在上面的例子里,你可能會爭論說你可以直接特殊化Widget類的SmartPtr的構造與析構函數。你可以使用把模板特殊化技術用在 SmartPtr本身,而不是用在traits上頭,這樣還可以消除額外的類。盡管對這個問題來說這種想法沒錯,但還是由一些你需要注意的缺陷:
            1. 這么干缺乏可擴展性。如果給SmartPtr再增加一個模板參數,你不能特殊化這樣一個SmartPtr<T. U>,其中模板參數T是Widget,而U可以為其他任何類型。
            2. 最終代碼不那么清晰。Trait有一個名字,而且把相關的東西很好的組織起來,因此使用traits的代碼更加容易理解。相比之下,用直接特殊化SmartPtr成員函數的代碼,看上去更招黑客的喜歡。
                  用繼承機制的解決方案,就算本身完美無瑕,也至少存在上述的缺陷。解決這樣一個變體問題,使用繼承實在是太笨重了。此外,通常用以取代繼承方案的另一種經典機制——containment,用在這里也顯得畫蛇添足,繁瑣不堪。相反,traits方案干凈利落,簡明有效,物合其用,恰到好處。
                  Traits的一個重要的應用是“interface glue”(接口膠合劑),通用的、可適應性極強的適配子。如果不同的類對于一個給定的概念有著不同的實現,traits可以把這些實現再組織統一成一個公共的接口。對于一個給定類型提供多種TRAITS:現在,我們假設所有的人都很喜歡你的SmartPtr模板類,直到有一天,在你的多線程應用程序里開始現了神秘的bug。你發現罪魁禍首是Widget,它的引用計數函數并不是線程安全的。現在你不得不親自實現Widget:: AddReference和Widget::RemoveReference,最合理的位置應該是在RefCountingTraits中,打上個補丁吧:
            // Example 7: Patching Widget's traits for thread safety
            template <>
            class RefCountingTraits<Widget>
            {
            static void Refer(Widget* p)
            {
            Sentry s(lock_); // serialize access
            p->AddReference();
            }
            static void Unrefer(Widget* p)
            {
            Sentry s(lock_); // serialize access
            if (p->RemoveReference() == 0)
            delete p;
            }
            private:
            static Lock lock_;
            };
                   不幸的是,雖然你重新編譯、測試之后正確運行,但是程序慢得像蝸牛。仔細分析之后發現,你剛才的所作所為往程序里塞了一個糟糕的瓶頸。實際上只有少數幾個Widget是需要能夠被好幾個線程訪問的,余下的絕大多數Widget都是只被一個線程訪問的。你要做的是告訴編譯器按你的需求分別使用多線程traits和單線程traits這兩個不同版本。你的代碼主要使用單線程traits。
                   如何告訴編譯器使用哪個traits?一種方法是把traits作為另一個模板參數傳給SmartPtr。缺省情況下傳遞老式的traits模板,而用特定的類型實例化特定的模板。
            template <class T, class RCTraits = RefCountingTraits<T> >
            class SmartPtr
            {

            };
                  你對單線程版的RefCountingTraits<Widget>不做改動,而把多線程版放在一個單獨的類中:
            class MtRefCountingTraits
            {
            static void Refer(Widget* p)
            {
            Sentry s(lock_); // serialize access
            p->AddReference();
            }
            static void Unrefer(Widget* p)
            {
            Sentry s(lock_); // serialize access
            if (p->RemoveReference() == 0)
            delete p;
            }
            private:
            static Lock lock_;
            };
                   現在你可將SmartPtr<Widget>用于單線程目的,將SmartPtr<Widget,MtRefCountingTraits>用于多線程目的。
                   最后,以SGI STL中的__type_traits結束本篇討論,在SGI 實現版的STL中,為了獲取高效率,提供了__type_traits,用來提取類的信息,比如類是否擁有trival的構造、析構、拷貝、賦值操作,然后跟據具體的信息,就可提供最有效率的操作。以下摘錄cygwin的gcc3.3源碼,有改動,在<type_traits.h>中。
            struct __true_type {};
            struct __false_type {};
            template <class _Tp>
            struct __type_traits
            {
            typedef __true_type    this_dummy_member_must_be_first;
            typedef __false_type    has_trivial_default_constructor;
            typedef __false_type    has_trivial_copy_constructor;
            typedef __false_type    has_trivial_assignment_operator;
            typedef __false_type    has_trivial_destructor;
            typedef __false_type    is_POD_type;
            };
                    對于普通類來講,為了安全起見,都認為它們擁有non-trival的構造、析構、拷貝、賦值函數,POD是指plain old data。接下來對C++的原生類型(bool,int, double之類)定義了顯式的特化實現,以double為例:
            template<> 
            struct __type_traits<long double> {
            typedef __true_type    has_trivial_default_constructor;
            typedef __true_type    has_trivial_copy_constructor;
            typedef __true_type    has_trivial_assignment_operator;
            typedef __true_type    has_trivial_destructor;
            typedef __true_type    is_POD_type;
            };
            還有,對所有的原生指針來講,它們的構造、析構等操作也是trival的,因此有:
            template <class _Tp>
            struct __type_traits<_Tp*> {
            typedef __true_type    has_trivial_default_constructor;
            typedef __true_type    has_trivial_copy_constructor;
            typedef __true_type    has_trivial_assignment_operator;
            typedef __true_type    has_trivial_destructor;
            typedef __true_type    is_POD_type;
            };
                  簡化<stl_algobase.h>中copy的部分代碼來說明對__type_traits的應用。
            template<typename _Tp>
            inline _Tp* __copy_trivial(const _Tp* __first, const _Tp* __last, _Tp* __result)
            {
            memmove(__result, __first, sizeof(_Tp) * (__last - __first));
            return __result + (__last - __first);
            }
            template<typename _Tp>
            inline _Tp* __copy_aux (_Tp* __first, _Tp* __last, _Tp* __result, __true_type)
            return __copy_trivial(__first, __last, __result); } 
            template<typename _Tp>
            inline _Tp* __copy_aux (_Tp* __first, _Tp* __last, _Tp* __result, __false_type)
            { 另外處理;}
            template<typename _InputIter, typename _OutputIter> inline 
            _OutputIter copy (_InputIter __first, _InputIter __last, _OutputIter __result)
            {
            typedef typename iterator_traits<_InputIter>::value_type _ValueType;
            typedef typename __type_traits<_ValueType>::has_trivial_assignment_operator _Trivial;
            return __copy_aux(__first, __last, __result, _Trivial());
            }
                  Copy 函數利用__type_traits判斷當前的value_type是否有trival的賦值操作,如果是,則產生類__true_type的實例,編譯 時選擇__copy_trivial函數進行memmove,效率最高。如果是non-trival的賦值操作,則另作處理,效率自然低些。__true_type和__false_type之所以是類,就因為C++的函數重載是根據類型信息來的,不能依據參數值來判別。使用SGI STL時,可以為自己的類定義__type_traits顯式特化版本,以求達到高效率。
            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/budTang/archive/2008/05/06/2397013.aspx
            中文字幕乱码久久午夜| 亚洲va中文字幕无码久久| 久久99亚洲综合精品首页| 久久精品国产精品国产精品污| 国产偷久久久精品专区 | 亚洲国产成人久久精品影视 | 久久精品人人做人人爽97 | 99久久精品国产一区二区三区| 色综合久久综合网观看| 久久久久18| 国产一区二区三区久久| 亚洲AⅤ优女AV综合久久久| 国产国产成人精品久久| 无码任你躁久久久久久| 久久精品嫩草影院| 国内精品九九久久精品 | 久久不见久久见免费视频7| 久久99精品国产99久久6| 久久精品国产亚洲av麻豆小说| 久久人人爽人人爽人人片AV麻豆| 日韩人妻无码一区二区三区久久| 激情综合色综合久久综合| 国产综合久久久久| 成人久久免费网站| 伊人久久五月天| 久久精品亚洲福利| 一级做a爰片久久毛片16| 久久99国产亚洲高清观看首页 | www性久久久com| 色欲av伊人久久大香线蕉影院| 亚洲午夜无码AV毛片久久| 久久精品国产99国产精品| 一本大道久久a久久精品综合| 久久久噜噜噜久久熟女AA片| 精品久久亚洲中文无码| 伊人久久大香线蕉亚洲| 伊人久久精品无码二区麻豆| 精品无码久久久久国产动漫3d| 久久精品国产免费观看三人同眠| 国产精品99久久久精品无码| 国产香蕉久久精品综合网|