• <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>

            woaidongmao

            文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見(jiàn)諒!~
            隨筆 - 1469, 文章 - 0, 評(píng)論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            Traits: 類(lèi)型的else-if-then機(jī)制

            什么是traits,為什么人們把它認(rèn)為是C++ Generic Programming的重要技術(shù)?

             

            簡(jiǎn)短截說(shuō),traits如此重要,是因?yàn)榇隧?xiàng)技術(shù)允許系統(tǒng)在編譯時(shí)根據(jù)類(lèi)型作一些決斷,就好像在運(yùn)行時(shí)根據(jù)值來(lái)作出決斷一樣。更進(jìn)一步,此技術(shù)遵循“另增一個(gè)間接層”的諺語(yǔ),解決了不少軟件工程問(wèn)題,traits使您能根據(jù)其產(chǎn)生的背景(context)來(lái)作出抉擇。這樣最終的代碼就變得清晰易讀,容易維護(hù)。如果你正確運(yùn)用了traits技術(shù),你就能在不付出任何性能和安全代價(jià)的同時(shí)得到這些好處,或者能夠契合其他解決方案上的需求。

             

            例子:Traits不僅是泛型程序設(shè)計(jì)的核心工具,而且我希望以下的例子能夠使你相信,在非常特定的問(wèn)題中,它也是很有用的。假設(shè)你現(xiàn)在正在編寫(xiě)一個(gè)關(guān)系數(shù)據(jù)庫(kù)應(yīng)用程序。可能您一開(kāi)始用數(shù)據(jù)庫(kù)供應(yīng)商提供的API庫(kù)來(lái)進(jìn)行反問(wèn)數(shù)據(jù)庫(kù)的操作。但是理所當(dāng)然的,不久之后你會(huì)感到不得不寫(xiě)一些包裝函數(shù)來(lái)組織那些原始的API,一方面是為了簡(jiǎn)潔,另一方面也可以更好地適應(yīng)你手上的任務(wù)。這就是生活的樂(lè)趣所在,不是嗎?

             

            一個(gè)典型的API是這樣的:提供一個(gè)基本的方法用來(lái)把游標(biāo)(cursor, 一個(gè)行集和或者查詢結(jié)果)處的原始數(shù)據(jù)傳送到內(nèi)存中。現(xiàn)在我們來(lái)寫(xiě)一個(gè)高級(jí)的函數(shù),用來(lái)把某一列的值取出來(lái),同時(shí)避免暴露底層的細(xì)節(jié)。這個(gè)函數(shù)可能會(huì)是這個(gè)樣子:

            (假想的DB APIdbDB開(kāi)頭)

             

            // Example 1: Wrapping a raw cursor int fetch

            // operation.

            // Fetch an integer from the

            //     cursor "cr"

            //     at column "col"

            //     in the value "val"

            void FetchIntField(db_cursor& cr,

                unsigned int col, int& val)

            {

                // Verify type match

                if (cr.column_type[col] != DB_INTEGER)

                    throw std::runtime_error(

                    "Column type mismatch");

                // Do the fetch

                db_integer temp;

                if (!db_Access_column(&cr, col))

                    throw std::runtime_error(

                    "Cannot transfer data");

                memcpy(&temp, cr.column_data[col],

                    sizeof(temp));

                // Required by the DB API for cleanup

            db_release_column(&cr, col);

                // Convert from the database native type to int

                val = static_cast<int>(temp);

            }

             

            這種接口函數(shù)我們所有人都可能不得不在某個(gè)時(shí)候?qū)懮弦槐椋缓脤?duì)付但又非常重要,處理了大量細(xì)節(jié),而且這還只是一個(gè)簡(jiǎn)單的例子。FetchIntField抽象,提供了高一層次的功能,它能夠從游標(biāo)處取得一個(gè)整數(shù),不必再擔(dān)心那些紛繁的細(xì)節(jié)。既然這個(gè)函數(shù)如此有用,我們當(dāng)然希望盡可能重用它。但是怎么做?一個(gè)很重要的泛化步驟就是讓這個(gè)函數(shù)能夠處理int之外的類(lèi)型。為了做到這一點(diǎn),我們得仔細(xì) 考慮代碼中跟int類(lèi)型相關(guān)的部分。但首先,DB_INTEGERdb_integer是什么意思,它們是打哪兒來(lái)的?是這樣,關(guān)系數(shù)據(jù)庫(kù)供應(yīng)商通常隨 API提供一些type-mapping helpers,為其所支持的每種類(lèi)型和簡(jiǎn)單的結(jié)構(gòu)定義一個(gè)符號(hào)常量或者typedef,把數(shù)據(jù)庫(kù)類(lèi)型對(duì)應(yīng)到C/C++類(lèi)型上。下面是一段假想的數(shù)據(jù)庫(kù)API頭文件:

             

            #define DB_INTEGER 1

            #define DB_STRING 2

            #define DB_CURRENCY 3

            ...

            typedef long int db_integer;

            typedef char     db_string[255];

            typedef strUCt {

                int integral_part;

                unsigned char fractionary_part;

            } db_currency;

            ...

             

            我們?cè)囍鴣?lái)寫(xiě)一個(gè)FetchDoubleField函數(shù),作為走向泛型化的第一步。此函數(shù)從游標(biāo)處得到一個(gè)double值。數(shù)據(jù)庫(kù)本身提供的類(lèi)型映像 (type mapping)db_currency,但是我們希望能用double的形式來(lái)操作。FetchDoubleField看上去跟 FetchIntField很相似,簡(jiǎn)直就是孿生兄弟。例2

             

            // Example 2: Wrapping a raw cursor double fetch operation.

            //

            void FetchDoubleField(db_cursor& cr, unsigned int col, double& val)

            {

                if (cr.column_type[col] != DB_CURRENCY)

                    throw std::runtime_error("Column type mismatch");

                if (!db_access_column(&cr, col))

                    throw std::runtime_error("Cannot transfer data");

                db_currency temp;

                memcpy(&temp, cr.column_data[col], sizeof(temp));

                db_release_column(&cr, col);

                val = temp.integral_part + temp.fractionary_part / 100.;

            }

             

            看上去很像FetchIntField吧!我們可不想對(duì)每一個(gè)類(lèi)型都寫(xiě)一個(gè)單獨(dú)的函數(shù),所以如果能夠在一個(gè)地方把FetchIntField, FetchDoubleField以及其他的Fetch函數(shù)合為一體就好了。我們把這兩片代碼的不同之處列舉如下:

             

            ·輸入類(lèi)型:double/int

            ·內(nèi)部類(lèi)型:db_currency/db_integer

            ·常數(shù)值類(lèi)型:DB_CURRENCY/DB_INTEGER

            ·算法:一個(gè)表達(dá)式/static_cast

             

            輸入類(lèi)型(int/double)與其他幾點(diǎn)之間的對(duì)應(yīng)關(guān)系看上去沒(méi)什么規(guī)律可循,而是很隨意,跟數(shù)據(jù)庫(kù)供應(yīng)商(恰好)提供的類(lèi)型關(guān)系密切。 Template機(jī)制本身無(wú)能為力,它沒(méi)有提供如此先進(jìn)的類(lèi)型推理機(jī)制。也沒(méi)法把不同的類(lèi)型用繼承關(guān)系組織起來(lái),因?yàn)槲覀兲幚淼氖窃碱?lèi)型。受到API的 限制以及問(wèn)題本身的底層特性,乍看上去我們好像沒(méi)轍了。不過(guò)我們還有一條活路。

             

            進(jìn)入TRAITS大門(mén):Traits技術(shù)就是用來(lái)解決上述問(wèn)題的:把與各種類(lèi)型相關(guān)的代碼片斷合體,并且具有類(lèi)似and/or結(jié)構(gòu)的能力,到時(shí)可以根據(jù)不同的類(lèi)型產(chǎn)生不同的變體。Traits依賴(lài)顯式模版特殊化(eXPlicit template specialization)機(jī)制來(lái)獲得這種結(jié)果。這一特性使你可以為每一個(gè)特定的類(lèi)型提供模板類(lèi)的一個(gè)單獨(dú)實(shí)現(xiàn),見(jiàn)例3

             

            // Example 3: A traits example

            //

            template <class T>

            class SomeTemplate

            {

                // generic implementation (1)

                ...

            };

             

            // 注意下面特異的語(yǔ)法

            template <>

            class SomeTemplate<char>

            {

                // implementation tuned for char (2)

                ...

            };

            ...

            SomeTemplate<int> a;      // will use (1)

            SomeTemplate<char*> b;    // will use (1)

            SomeTemplate<char> c;     // will use (2)

             

            如果你用char類(lèi)型來(lái)實(shí)例化SomeTemplate類(lèi)模板,編譯器會(huì)用那個(gè)顯式的模板聲明來(lái)特殊化。至于其他的類(lèi)型,當(dāng)然就用那個(gè)通用模板來(lái)實(shí)例化。這就像一個(gè)由類(lèi)型驅(qū)動(dòng)if-statement。通常最通用的模板(相當(dāng)于else部分)最先定義,if-statement靠后一點(diǎn)。你甚至可以決定完全不提供通用的模板,這樣只有特定的實(shí)例化是允許的,其他的都會(huì)導(dǎo)致編譯錯(cuò)誤。

             

            現(xiàn)在我們把這個(gè)語(yǔ)言特性跟手上的問(wèn)題聯(lián)系起來(lái)。我們要實(shí)現(xiàn)一個(gè)模板函數(shù)FetchField,用需要讀取的類(lèi)型作為參數(shù)來(lái)實(shí)例化。在該函數(shù)內(nèi)部,我會(huì)用一個(gè)叫做TypeId的東西代表那個(gè)符號(hào)常量,當(dāng)要獲取int型 值時(shí)它的值就是DB_INTEGER,當(dāng)要獲取double型值時(shí)它的值就是DB_CURRENCY。否則,就必須在編譯時(shí)報(bào)錯(cuò)。類(lèi)似的,根據(jù)要獲取的類(lèi)型的不同,我們還需要操作不同的API類(lèi)型(db_integer/db_currency)和不同的轉(zhuǎn)換算法(表達(dá)式/static_cast).讓我們用顯式模板特殊化機(jī)制來(lái)解決這個(gè)問(wèn)題。我們得有一個(gè)FetchField,可以針對(duì)一個(gè)模板類(lèi)來(lái)產(chǎn)生不同的變體,而那個(gè)模板類(lèi)又能夠針對(duì)intdouble進(jìn)行顯式特殊化。每個(gè)特殊化都必須為這些變體提供統(tǒng)一的名稱(chēng)。

             

            // Example 4: Defining DbTraits

            //

            // Most general case not implemented 最通用的情況沒(méi)有實(shí)現(xiàn)

            template <typename T> struct DbTraits;

            // Specialization for int

            template <>

            struct DbTraits<int>

            {

                enum { TypeId = DB_INTEGER };

                typedef db_integer DbNativeType;

            // 注意下面的Convertstatic member function —— 譯者

                static void Convert(DbNativeType from, int& to)

                {

                    to = static_cast<int>(from);

                }

            };

            // Specialization for double

            template <>

            struct DbTraits<double>

            {

                enum { TypeId = DB_CURRENCY };

                typedef db_currency DbNativeType;

                // 注意下面的Convertstatic member function —— 譯者

                static void Convert(const DbNativeType& from, double& to)

                {

                    to = from.integral_part + from.fractionary_part / 100.;

                }

            };

             

            現(xiàn)在,如果你寫(xiě)DbTraits<int>::TypeId,你得到的就是DB_INTEGER,而對(duì)于DbTraits<double>::TypeId,得到的就是DB_CURRENCY,對(duì)于DbTraits<anything_else>::TypeId,得到的是什么呢?Compile-time error!因?yàn)槟0孱?lèi)本身只是聲明了,并沒(méi)有定義。是不是一勞永逸了?看看我們?nèi)绾卫?span lang="EN-US">DbTraits來(lái)實(shí)現(xiàn)FetchField就放心了。我們把所有變化的部分——枚舉類(lèi)型、API類(lèi)型、轉(zhuǎn)換算法——都放在了DbTraits里,這下我們的函數(shù)里只包含FetchIntFieldFetchDoubleField的相同部分了:

             

            // Example 5: A generic, extensible FetchField using DbTraits

            //

            template <class T>

            void FetchField(db_cursor& cr, unsigned int col, T& val)

            {

                // Define the traits type

                typedef DbTraits<T> Traits;

                if (cr.column_type[col] != Traits::TypeId)

                    throw std::runtime_error("Column type mismatch");

                if (!db_access_column(&cr, col))

                    throw std::runtime_error("Cannot transfer data");

                typename Traits::DbNativeType temp;

                memcpy(&temp, cr.column_data[col], sizeof(temp));

                Traits::Convert(temp, val);

                db_release_column(&cr, col);

            }

             

            搞定了!我們只不過(guò)實(shí)現(xiàn)和使用了一個(gè)traits模板類(lèi)而已!Traits依靠顯式模板特殊化來(lái)把代碼中因類(lèi)型不同而發(fā)生變化的片斷拖出來(lái),用統(tǒng)一的接口來(lái)包裝。這個(gè)接口可以包含一個(gè)C++類(lèi)所能包含的任何東西:內(nèi)嵌類(lèi)型,成員函數(shù),成員變量,作為客戶的模板代碼可以通過(guò)traits模板類(lèi)所公開(kāi)的接口來(lái)間接訪問(wèn)之。這樣的traits接口通常是隱式的,隱式接口不如函數(shù)簽名(function signatures)那么嚴(yán)格,例如,盡管DbTraits<int>::ConvertDbTraits<double>::Convert有著非常不同的簽名,但它們都可以正常工作。 Traits模板類(lèi)在各種類(lèi)型上建立一個(gè)統(tǒng)一的接口,而又針對(duì)各種類(lèi)型提供不同的實(shí)現(xiàn)細(xì)節(jié)。由于Traits抓住了一個(gè)概念,一個(gè)相關(guān)聯(lián)的選擇集,所以能夠在相似的contexts中被重用。

             

            定義: A traits template is a template class, possibly explicitly

            specialized, that provides a uniform symbolic interface over a coherent

            set of design choices that vary from one type to another.

                  

                Traits模板是一個(gè)模板類(lèi),很可能是顯式特殊化的模板類(lèi),它為一系列根據(jù)不同類(lèi)型做出的設(shè)計(jì)選擇提供了一個(gè)統(tǒng)一的、符號(hào)化的接口。TRAITS AS ADAPTERS: 用作適配子的TRAITS數(shù)據(jù)庫(kù)的問(wèn)題說(shuō)得夠多了,現(xiàn)在我們來(lái)看一個(gè)更一般的例子——smart pointers。假 設(shè)你正在設(shè)計(jì)一個(gè)SmartPtr模板類(lèi)。對(duì)于一個(gè)smart pointer來(lái)說(shuō),最美妙的事情是它們可以自動(dòng)管理內(nèi)存問(wèn)題,同時(shí)在其他方面又像一個(gè)常規(guī)指針。而不那么美妙的事情是,它們的實(shí)現(xiàn)代碼可不是好對(duì)付的,有些C++smart pointer實(shí)現(xiàn)技術(shù)簡(jiǎn)直就是在黑暗中變戲法。這一殘酷的事實(shí)帶來(lái)了一個(gè)重要實(shí)踐經(jīng)驗(yàn):你最好盡一切可能一勞永逸,寫(xiě)出一個(gè)出色的、具有工業(yè)強(qiáng)度的 smart pointer來(lái)滿足你所有的需求。此外,你通常不能修改一個(gè)類(lèi)來(lái)適應(yīng)你的smart pointer,所以你的SmartPtr一定要足夠靈活。

             

            有不少類(lèi)層次使用引用計(jì)數(shù)(reference counting)以及相應(yīng)的函數(shù)管理對(duì)象的生存期。然而,并沒(méi)有reference counting的標(biāo)準(zhǔn)實(shí)現(xiàn)方法,每一個(gè)C++庫(kù)的供應(yīng)商在實(shí)現(xiàn)的語(yǔ)法和/或語(yǔ)義上都有所不同。例如,在你的應(yīng)用程序中有這樣兩個(gè)interfaces:大部分的類(lèi)實(shí)現(xiàn)了RefCounted接口:

            class RefCounted

            {

            public:

                void IncRef() = 0;

                bool DecRef() = 0; // if you DecRef() to zero

                    // references, the object is destroyed

                    // automatically and DecRef() returns true

                virtual ~RefCounted() {}

            };

             

            而由第三方提供的Widget類(lèi)使用不同的接口:

            class Widget

            {

            public:

                void AddReference();

                int RemoveReference(); // returns the remaining

                    // number of references; it's the client's

                    // responsibility to destroy the object

                ...

            };

             

            不過(guò)你并不想維護(hù)兩個(gè)smart pointer類(lèi),你想讓兩種類(lèi)共享一個(gè)SmartPtr。一個(gè)基于traits的解決方案把兩種不同的接口用語(yǔ)法和語(yǔ)義上統(tǒng)一的接口包裝起來(lái),建立針對(duì)普通類(lèi)的通用模板,而針對(duì)Widget建立一個(gè)特殊化版本,如下:

             

            // Example 6: Reference counting traits

            //

            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_);

                }

            }

             

            當(dāng)然在上面的例子里,你可能會(huì)爭(zhēng)論說(shuō)你可以直接特殊化Widget類(lèi)的SmartPtr的構(gòu)造與析構(gòu)函數(shù)。你可以使用把模板特殊化技術(shù)用在SmartPtr 本身,而不是用在trait上頭,這樣還可以消除額外的類(lèi)。盡管對(duì)這個(gè)問(wèn)題來(lái)說(shuō)這種想法沒(méi)錯(cuò),但還是由一些你需要注意的缺陷:[譯者為了說(shuō)明起見(jiàn),提供一個(gè)針對(duì)SmartPtr本身進(jìn)行顯式特殊化的范例供作者批判:]

            // 通用類(lèi)型版

            template <class T>

            class SmartPtr {

            private:

                T *pointee_;

            public:

                SmartPtr(T* pobj) {

            pointee_ = pobj;

            pobj->IncRef();

                }

                ...

                ~SmartPtr() {

            pointee_->DecRef();

                }

            };

             

            // Widget類(lèi)專(zhuān)用版

            template <>

            class SmartPtr<Widget> {

            private:

                T *pointee_;

            public:

                SmartPtr(T* pobj) {

            pointee_ = pobj;

            pobj->AddReference();

                }

                ...

                ~SmartPtr() {

            if (pointee_->RemoveReference() == 0)

                 delete pointee_;

                }

            };

             

            這么干缺乏可擴(kuò)展性。如果給給SmartPtr再增加一個(gè)模板參數(shù),喏,全完蛋了!你不能特殊    化這樣一個(gè)SmartPtr<T. U>,其中模板參數(shù)TWidget,而U可以為其他任何類(lèi)型。不,你做不到。附帶說(shuō)一句,smart pointer經(jīng)常用作模板參數(shù)。[譯者附釋?zhuān)阂簿褪钦f(shuō),顯式模板特殊化的if-statement部分,針對(duì)特別的模板參數(shù)提供特別 的特殊化方案,要求定義時(shí)必須指定全部的參數(shù)類(lèi)型,不能只是“部分指定”。例如:

            // else-part, 針對(duì)其他所有類(lèi)型

            template <class T, class U>

            class Demo {...};

             

            template <>

            class Demo<type1, type2> {...}; //可以,TU都指定了

             

            template <>

            class Demo<type3, U> {...}; // 不行,不能只指定部分模板參數(shù)

             

            template <class U>

            class Demo<type4, U> {...}; // 想蒙混過(guò)關(guān)?對(duì)不住,C++不允許]

             

            ·最終代碼不那么清晰。Trait有一個(gè)名字,而且把相關(guān)的東西很好的組織起來(lái),因此使用traits的代碼更加容易理解。相比之下,用直接特殊化SmartPtr成員函數(shù)的代碼,看上去更招黑客的喜歡。

             

            ·你沒(méi)法對(duì)一種類(lèi)型使用多種traits

             

            用繼承機(jī)制的解決方案,就算本身完美無(wú)瑕,也至少存在上述的缺陷。解決這樣一個(gè)變體問(wèn)題,使用繼承實(shí)在是太笨重了。此外,通常用以取代繼承方案的另一種經(jīng)典機(jī)制——containment,用在這里也顯得畫(huà)蛇添足,繁瑣不堪。相反,traits方案干凈利落,簡(jiǎn)明有效,物合其用,恰到好處。

             

            Traits的一個(gè)重要的應(yīng)用是“interface glue”(接口膠合劑),通用的、可適應(yīng)性極強(qiáng)的適配子。如果不同的類(lèi)對(duì)于一個(gè)給定的概念有著不同的實(shí)現(xiàn),traits可以把這些實(shí)現(xiàn)再組織統(tǒng)一成一個(gè)公共的接口。對(duì)于一個(gè)給定類(lèi)型提供多種TRAITS:現(xiàn)在,我們假設(shè)所有的人都很喜歡你的SmartPtr模板類(lèi),直到有一天,在你的多線程應(yīng)用程序里開(kāi)始現(xiàn)了神秘的 bug,美夢(mèng)被驚醒了。你發(fā)現(xiàn)罪魁禍?zhǔn)资?span lang="EN-US">Widget,它的引用計(jì)數(shù)函數(shù)并不是線程安全的。現(xiàn)在你不得不親自實(shí)現(xiàn)Widget:: AddReferenceWidget::RemoveReference,最合理的位置應(yīng)該是在RefCountingTraits中,打上個(gè)補(bǔ)丁 吧:

             

            // 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_;

            };

             

            不幸的是,雖然你重新編譯、測(cè)試之后正確運(yùn)行,但是程序慢得像蝸牛。仔細(xì)分析之后發(fā)現(xiàn),你剛才的所作所為往程序里塞了一個(gè)糟糕的瓶頸。實(shí)際上只有少數(shù)幾個(gè) Widget是需要能夠被好幾個(gè)線程訪問(wèn)的,余下的絕大多數(shù)Widget都是只被一個(gè)線程訪問(wèn)的。你要做的是告訴編譯器按你的需求分別使用多線程 traits和單線程traits這兩個(gè)不同版本。你的代碼主要使用單線程traits.如何告訴編譯器使用那個(gè)traits?這么干:把traits作為另一個(gè)模板參數(shù)傳給SmartPtr。缺省情況下傳遞老式的traits模板,而用特定的類(lèi)型實(shí)例化特定的模板。

             

            template <class T, class RCTraits = RefCountingTraits<T> >

            class SmartPtr

            {

                ...

            };

             

            你對(duì)單線程版的RefCountingTraits<Widget>不做改動(dòng),而把多線程版放在一個(gè)單獨(dú)的類(lèi)中:

             

            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_;

            };

             

            現(xiàn)在你可將SmartPtr<Widget>用于單線程目的,將SmartPtr<Widget, MtRefCountingTraits>用于多線程目的。這就是了!就跟Scott Meyers可能會(huì)說(shuō)的那樣,“你要是沒(méi)體會(huì)過(guò)快樂(lè),就不知道怎么找樂(lè)。”如果一種類(lèi)型只要一個(gè)trait由可以應(yīng)付,那么只要用顯式模板特殊化就夠了。現(xiàn)在即使一個(gè)類(lèi)型需要多個(gè)trait來(lái)應(yīng)付我們也搞得定。所以,traits必須能夠從外界塞進(jìn)來(lái),而不是在內(nèi)部“算出來(lái)”。一個(gè)應(yīng)當(dāng)謹(jǐn)記的慣用法是提供一個(gè)traits類(lèi)作為最后一個(gè)模板參數(shù)。缺省的traits通過(guò)模板參數(shù)缺省值給定。定義:一個(gè)traits類(lèi)(與traits模板類(lèi)相對(duì))或者是一個(gè)traits模板類(lèi)的實(shí)例,或者是一個(gè)與traits模板類(lèi)展現(xiàn)出相同接口的單獨(dú)的類(lèi)。

             

             

            posted on 2008-11-09 01:30 肥仔 閱讀(883) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): C++ 模板

            中文字幕亚洲综合久久2| 色偷偷88888欧美精品久久久| 久久综合狠狠综合久久综合88 | 亚洲人AV永久一区二区三区久久| 超级碰碰碰碰97久久久久| 麻豆精品久久久一区二区| 精品蜜臀久久久久99网站| 99久久99久久精品国产片果冻 | 午夜精品久久影院蜜桃| 久久久久久久综合综合狠狠| 久久亚洲精品成人av无码网站| 久久久久综合网久久| 人妻精品久久久久中文字幕一冢本| 99久久综合狠狠综合久久| 久久精品中文无码资源站| 18岁日韩内射颜射午夜久久成人| 国产精品成人99久久久久91gav| 99久久中文字幕| 精品少妇人妻av无码久久| 欧洲人妻丰满av无码久久不卡| 久久久久高潮综合影院| 久久一日本道色综合久久| 色综合久久久久久久久五月| 日韩人妻无码精品久久久不卡| 色偷偷久久一区二区三区| 久久久久高潮毛片免费全部播放| 亚洲精品乱码久久久久久中文字幕 | 久久国产精品免费| 久久强奷乱码老熟女网站| 思思久久精品在热线热| 久久综合给久久狠狠97色| 久久五月精品中文字幕| 免费久久人人爽人人爽av| 精品久久久久久中文字幕| 久久e热在这里只有国产中文精品99 | 久久综合九色综合网站| 久久久久噜噜噜亚洲熟女综合| 久久无码高潮喷水| 久久精品亚洲福利| 国内精品久久国产大陆| 欧美牲交A欧牲交aⅴ久久|