欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
boost源碼剖析之:泛型函數指針類(lèi)boost::function(rev#3)

boost源碼剖析之:泛型函數指針類(lèi)boost::function(rev#3)

 

劉未鵬

C++的羅浮宮(http://blog.csdn.net/pongba)

 

Note: 并非新作,03年曾放在blog上,現在這個(gè)版本應該是修改后的最終版本。

 

前奏

如你所知,boost庫是個(gè)特性完備,且具備工業(yè)強度的庫,眾多C++權威的參與使其達到了登峰造極的程度。尤其泛型的強大威力在其中被發(fā)揮得淋漓盡致,令人瞠目結舌。

 

然而弱水三千,我們只取一瓢飲。下面,我試圖從最單純的世界開(kāi)始,一步一步帶領(lǐng)你進(jìn)入源碼的世界,去探究boost::function(下文簡(jiǎn)稱(chēng)function)內部的精微結構。

 

通常 ,在單純的情況下,對函數的調用簡(jiǎn)單而且直觀(guān),像這樣:

 

int fun(int someVal);

  int main(){

    fun(10);

  }

 

然而你可能需要在某個(gè)時(shí)刻將函數指針保存下來(lái),并在以后的另一個(gè)時(shí)刻調用它,像這樣:

 

  int fun(int);

  typedef int (*func_handle)(int);

  int main(){

    func_handle fh=fun;

    ...  //do something

    fh(10);

  }

 

但是,如果fun形式為void fun(int)呢?如你所見(jiàn),fun可能有無(wú)數種形式,如果對fun的每一個(gè)形式都typedef一個(gè)對應的func_handle,則程序員會(huì )焦頭爛額,不勝其擾,代碼也可能變得臃腫和丑陋不堪,甚至如果fun是仿函數呢?

 

幸運的是C++泛型可以使代碼變得優(yōu)雅精致,面對無(wú)數種的可能,泛型是最好的選擇。

 

因此,你只是需要一個(gè)能夠保存函數指針的泛型模板類(lèi)(對應于Command模式),因為泛型編程有一個(gè)先天性的優(yōu)勢——可以借助編譯器的力量在編譯期根據用戶(hù)提供的類(lèi)型信息化身千萬(wàn)(實(shí)例化),所以一個(gè)泛型的類(lèi)可以有無(wú)限個(gè)實(shí)例,也就是說(shuō)可以保存無(wú)限多種可能類(lèi)型的函數或類(lèi)似函數的東西(如仿函數)。這個(gè)類(lèi)(boost庫中的類(lèi)名為function)與函數指針相比應該有以下一些優(yōu)勢:

 

1. 同一個(gè)function對象應能夠接受與它形式兼容的所有函數和仿函數,例如:

 

int f1(int); // 這是個(gè)函數,形式為 int(int)

short f2(double); // 這個(gè)函數形式為 short(double)

  struct functor // 這是個(gè)仿函數類(lèi),形式為int(int)

  {

    int operator()(int){}

  };

functor f3; //創(chuàng )建仿函數對象

 

boost::function<int(int)> func; // 能接受int(int)型的函數或仿函數

func = f1;  // 接受f1

func(10); // 調用f1(10)

func = f2;  // 也能接受short(double)型的f2

func(10); // 調用f2(10)

func = f3;  // 也能接受仿函數f3

func(10); // 調用f3(10)

 

2. function應能夠和參數綁定以及其它function-construction庫協(xié)同工作。例如,function應該也能夠接受std::bind1st返回的仿函數。這一點(diǎn)其實(shí)由第一點(diǎn)已經(jīng)有所保證。

 

3. 當接受的一個(gè)空的仿函數對象被調用的時(shí)候function應該有可預期的行為。

 

顯然,第一點(diǎn)是我們的重點(diǎn),所謂形式兼容,就是說(shuō),對于:

 

R1 (T0,T1,T2,...,TN) => FunctionType1

R2 (P0,P1,P2,...,PN) => FunctionType2

 

兩種類(lèi)型的函數(廣義),只要滿(mǎn)足:

 

R2能夠隱式轉換為R1

所有Ti都能夠隱式轉換為Pi (i0,1,2,...)

 

那么就說(shuō),boost::function<FunctionType1>可以接受FunctionType2類(lèi)型的函數(注意,反之不行)。支持這一論斷的理由是,只要Ti能夠隱式轉型為Pi,那么參數被轉發(fā)給真實(shí)的函數調用就是安全的,并且如果R2能夠隱式轉型為R1,那么返回真實(shí)函數調用所返回的值就是安全的。這里安全的含義是,C++類(lèi)型系統假定隱式轉換不會(huì )丟失信息,或者編譯器至少會(huì )給出編譯警告。

 

后面你會(huì )看到,boost::function通過(guò)所謂的invoker非常巧妙地實(shí)現了這點(diǎn),并且阻止了被形式不兼容的函數賦值的操作。

 

探險

先看一個(gè)function的最簡(jiǎn)單的使用:

 

int g(int); // 為了讓代碼簡(jiǎn)單,假設g有定義,以后的代碼都會(huì )如此

function<int(int)> f(g);

f(0);

 

間奏——R(T1,T2,...)函數類(lèi)型

雖然這個(gè)間奏未免早了點(diǎn)兒,但是為了讓你以后不會(huì )帶著(zhù)迷惑,這是必要的。請保持耐心。

 

或許你會(huì )對模板參數int(int)感到陌生,其實(shí)它是個(gè)函數類(lèi)型——函數g確切類(lèi)型就是int(int),而我們通常所看到的函數指針類(lèi)型int (*)(int)則是&g的類(lèi)型。它們的區別與聯(lián)系在于:當把g作為一個(gè)值進(jìn)行拷貝的時(shí)候(例如,按值傳參),其類(lèi)型就會(huì )由int(int)退化為int(*)(int),即從函數類(lèi)型退化為函數指針類(lèi)型——因為從語(yǔ)義上說(shuō),函數不能被按值拷貝,但身為函數指針的地址值則是可以被拷貝的。另一方面,如果g被綁定到引用,則其類(lèi)型不會(huì )退化,仍保持函數類(lèi)型。例如:

 

template<class T>

void test_func_type(T ft) // 按值傳遞,類(lèi)型退化

{

// 故意引發(fā)編譯錯誤,在錯誤信息里看出ft的類(lèi)型為退化后的函數指針類(lèi)型

static_cast<int>(ft);

}

 

int g(int); // g確切類(lèi)型為int(int)

 

test_func_type(g);  // g的類(lèi)型將會(huì )退化為函數指針類(lèi)型

 

int (&ref_f)(int) = g; // 由于綁定到引用,類(lèi)型并不退化

 

當然,這樣的代碼不能通過(guò)編譯,因為static_cast顯然不會(huì )讓一個(gè)函數指針轉換為int,然而我們就是要它通不過(guò)編譯,這樣我們才能窺視到按值傳遞的參數ft的類(lèi)型到底是什么,從編譯錯誤中我們看出,ft的類(lèi)型是int(*)(int),也就是說(shuō),在按值傳遞的過(guò)程中,g的類(lèi)型退化為函數指針類(lèi)型,變得和&g的類(lèi)型一樣了。而ref_t的類(lèi)型則是引用,引用綁定則沒(méi)有引起類(lèi)型退化。

 

請注意,函數類(lèi)型乃是個(gè)極其特殊的類(lèi)型,在大多數時(shí)候它都會(huì )退化為函數指針類(lèi)型,以便滿(mǎn)足拷貝語(yǔ)義,只有面對引用綁定的時(shí)候,能夠維持原來(lái)的類(lèi)型。當然,對于boost::function,總是按值拷貝。

 

繼續旅程

function<int(int)>實(shí)際上進(jìn)行了模板偏特化,boost庫給function的類(lèi)聲明為:

 

template<

typename Signature,  //函數類(lèi)型

typename Allocator = ... //Allocator并非重點(diǎn),故不作介紹

> 

class function;

 

事實(shí)上function類(lèi)只是個(gè)薄薄的外覆(wrapper),真正起作用的是其偏特化版本。

 

對于function<R(T0)>形式,偏特化版本的function源碼像這樣(實(shí)際上在boost源代碼中你看不到模板參數T0的聲明,也看不到function1,它們被宏替換掉了,那些精巧的宏是為了減小可見(jiàn)的代碼量,至于它們的細節則又是一個(gè)世界,以下代碼可看作對將那些令人眼花繚亂的宏展開(kāi)后所得到的代碼,具有更好的可讀性)

 

摘自:”boost/function/function_template.hpp”

 

template<typename R,typename T0,typename Allocator>

class function<R(T0),Allocator> // R(T0)函數類(lèi)型的偏特化版本

:public function1<R,T0,Allocator> // R(T0)形式的函數準備的基類(lèi)

{

typedef function1<R,T0,Allocator> base_type;

typedef function selftype;

 

struct clear_type{}; // 馬上你會(huì )看到這個(gè)蹊蹺的類(lèi)型定義的作用

 

public:

// 模板化的構造函數,為了能夠接受形式兼容的仿函數對象。

// 這個(gè)構造函數的作用在下面解釋

template<typename Functor>

  function(Functor f,

typename enable_if<

             (ice_not<(is_same<Functor, int>::value)>::value),

             int

>::type = 0)

:base_type(f)

{}

 

// 這個(gè)構造函數的作用見(jiàn)下文解釋

function(clear_type*) : base_type() {}

   

...

};

 

boost::enable_if

你一定對模板構造函數中出現的那個(gè)冗長(cháng)的enable_if<...>的作用心存疑惑,其實(shí)它的作用說(shuō)穿了很簡(jiǎn)單,就是:當用戶(hù)構造:

 

function<int(int)> f(0);

 

的時(shí)候,將該(帶有enable_if的)構造函數從重載決議的候選集中踢掉。使重載決議的結果為選中第三個(gè)構造函數:

 

function(clear_type*):base_type(){}

 

從而進(jìn)行缺省構造。

 

而說(shuō)得冗長(cháng)一點(diǎn)就是:當f的類(lèi)型——Functor——不是int時(shí),該構造函數就是有效(enable的,會(huì )被重載決議選中。但如果用戶(hù)提供了一個(gè)0,用意是構造一個(gè)空(null)的函數指針,那么該函數就會(huì )由于“SFINAE”原則而被從重載決議的候選函數中踢掉。為什么要這樣呢?因為該構造函數負責把確切的f保存起來(lái),它假定f并非0。那應該選擇誰(shuí)呢?第三個(gè)構造函數!其參數類(lèi)型是clear_type*,當然,0可以被賦給任何指針,所以它被選出,執行缺省的構造行為。

 

基類(lèi) functionN

function的骨架就這些。也許你會(huì )問(wèn),function作為一個(gè)仿函數類(lèi),怎么沒(méi)有重載operator()——這可是身為仿函數的標志??!別急,function把這些煩人的任務(wù)都丟給了它的基類(lèi)functionN,根據情況不同,N可能為0,1,2...,說(shuō)具體一點(diǎn)就是:根據用戶(hù)使用function時(shí)給出的函數類(lèi)型,function將會(huì )繼承自不同的基類(lèi)——如果用戶(hù)給出的函數類(lèi)型為R()形式的,即僅有一個(gè)參數,則function繼承自function0,而對于R(T0)形式的函數類(lèi)型,則繼承自function1,依此類(lèi)推。前面說(shuō)過(guò),function只是一層外覆,而所有的秘密都在其基類(lèi)functionN中!

 

不知道你有沒(méi)有發(fā)現,function的骨架中也幾乎沒(méi)有用到函數類(lèi)型的信息,事實(shí)上,它也將這些信息一股腦兒拋給了基類(lèi)。在這過(guò)程中,混沌一團的int(int)類(lèi)型被拆解為兩個(gè)單獨的模板參數傳給基類(lèi):

 

template<typename R,typename T0,typename Allocator>

class function<R(T0),Allocator> // R(T0)整個(gè)為一類(lèi)型

:public function1<R,T0,Allocator> // 拆解為兩個(gè)模板參數R,T0傳給基類(lèi)

 

好了,下面我們深入基類(lèi)function1。真正豐富的寶藏在里面。

 

function1

function1的源代碼像這樣(與上面一樣,事實(shí)上有些代碼你是看不到的,為了不讓你迷惑,我給出的是將宏展開(kāi)后得到的代碼):

 

摘自:”boost/function/function_template.hpp”

 

template<typename R,typename T0,class Allocator = ...>

class function1

: public function_base // function_base負責管理內存

{

...

public:

typedef R result_type;   //返回類(lèi)型

typedef function1 self_type;

 

template<typename Functor>

function1(Functor const & f,

typename enable_if<...>::type = 0)

: function_base(), invoker(0)

{

this->assign_to(f);   //這兒真正進(jìn)行賦值,assign_to的代碼在下面列出

}

 

template<typename Functor>

void assign_to(Functor f) // 所有的構造函數都調用它!具有多個(gè)重載版本。

{

     // 以一個(gè)get_function_tag萃取出Functor的類(lèi)別(category)!

typedef typename detail::function::get_function_tag<Functor>::type

tag;

     

      // 根據不同類(lèi)別的Functor采取不同的assign策略!

this->assign_to(f, tag()); // 轉到真正做事情的assign_to版本,見(jiàn)下文。

}

 

...

result_type operator()(T0 a0) const // 身為仿函數的標志!

{

...

 

    // 這里進(jìn)行真正的函數調用,使用invoker,

// invokerfunctionN的成員變量,在下面的assign_to中被賦值。

// functor為實(shí)際被調用的函數或仿函數,a0當然是其參數。

    internal_result_type result = invoker(function_base::functor, a0);

   

// 將得到的結果cast至最終返回出去的類(lèi)型

return static_cast<result_type>(result);

}

 

其中值得注意的是:

get_function_tag<>能萃取出Functor的類(lèi)別(category),有下面幾種類(lèi)別

 

struct function_ptr_tag {}; // 函數指針

struct function_obj_tag {}; // 仿函數對象

struct member_ptr_tag {}; // 成員函數

struct function_obj_ref_tag {}; // ref(obj)加以封裝的類(lèi)別,具有引用語(yǔ)義

struct stateless_function_obj_tag {}; // 無(wú)狀態(tài)函數對象

 

此外,滿(mǎn)足以下所有條件:

 

has_trivial_constructor

has_trivial_copy

has_trivial_destructor

is_empty

 

的仿函數對象稱(chēng)為stateless的。

 

對于不同的函數類(lèi)別,assign_to有各個(gè)不同的重載版本,如下:

 

template<typename FunctionPtr> // 如果是函數指針就調用這個(gè)版本

void assign_to(FunctionPtr f, function_ptr_tag) // 這個(gè)版本針對函數指針

{

clear();

   

  if(f){

    typedef

typename ...::get_function_invoker1<

                  FunctionPtr,R,T0>::type

invoker_type; // 萃取相應的invoker

   

invoker = &invoker_type::invoke; // invoke是一個(gè)static成員函數

   

function_base::manager = // 管理策略,一個(gè)函數指針

      &...::functor_manager<FunctionPtr, Allocator>::manage;

 

// 交給function的函數指針或仿函數對象指針最終在這兒保存

function_base::functor =

function_base::manager(

...::make_any_pointer((void (*)())(f)),

        ...::clone_functor_tag); // 拷貝一份

}

}

  ...

 

  // any_pointer在下文解釋

typedef internal_result_type(*invoker_type)(...::any_pointer, T0);

 

invoker_type invoker; // 重要成員,負責調用函數!

 

}; // function1類(lèi)定義的結尾

 

function的底層存儲機制

請將目光轉向上面的代碼段末尾的assign_to函數中,其中有兩行代碼分別對function_base里的managerfunctor成員賦值。這兩行代碼肩負了保存各種函數指針的任務(wù)。

 

manager是一個(gè)函數指針,它所指向的函數代表管理策略,例如,對于函數指針,僅僅作一次賦值,就保存完畢了,但是對于仿函數,得額外分配一次內存,然后將仿函數拷貝到分配的內存中,這才完成了保存的任務(wù)。這些策略根據函數的類(lèi)別而定,上面代碼中的assign_to函數是針對函數指針類(lèi)別的重載版本,所以manager的策略是不作任何內存分配,直接返回被轉型為void(*)()(利于在底層以統一的形式保存)的函數指針就行了,這從代碼中可以看出。

 

需要說(shuō)明的是,對于函數指針,function_base并不知道也不關(guān)心它要保存的函數指針是什么確切的類(lèi)型,只要是函數指針就行,因為它總會(huì )把該函數指針f轉型為void (*)()類(lèi)型,然后保存在functor成員中,functor成員是一個(gè)union類(lèi)型:

 

union any_pointer

{

    // 任意仿函數對象指針都可以用static_cast轉型為void*

void* obj_ptr;

   

    // const仿函數準備的

const void* const_obj_ptr;

   

// 任意函數指針都可以用reinterpret_cast轉型為void(*)()

void (*func_ptr)();

 

char data[1];

};

 

這個(gè)any_pointer可以通過(guò)安全轉型保存所有形式的仿函數和函數指針,承載在底層保存數據的任務(wù)

 

function的調用機制——invoker

我們把目光轉到function1的定義的最底部,那兒定義了它最重要的成員invoker,它是一個(gè)函數指針,所指向的函數就是function的調用機制所在,invoker的類(lèi)型為:

 

typedef internal_result_type (*invoker_type)(any_pointer,T0);

 

前面已經(jīng)說(shuō)過(guò),any_pointer是個(gè)union,可以保存任何類(lèi)型的函數指針或函數對象,里面保存的是用戶(hù)給出的函數或仿函數,T0則是保存于any_pointer中的函數的參數類(lèi)型(若有兩個(gè)參數則是T1,T2)。這也就是說(shuō),invoker負責調用保存在any_pointer中的函數或仿函數。

 

那么,invoker這個(gè)函數指針到底指向什么函數呢——也就是說(shuō),在什么時(shí)候invoker被賦值了呢?我們再次把目光轉向assign_to函數,其中有一行對invoker成員賦值的語(yǔ)句,從這行語(yǔ)句出發(fā)我們可以揭開(kāi)invoker的全部奧秘:

 

invoker = &invoker_type::invoke; // invoke是一個(gè)static成員函數

 

請不要把這個(gè)invoker_type和上面那個(gè)函數指針類(lèi)型invoker_type混淆起來(lái),這個(gè)invoker_type是位于assign_to函數中的一個(gè)局部的typedef,所以隱藏了后者(即類(lèi)作用域中的那個(gè)invoker_type——invoker成員的類(lèi)型)。往上一行,你就看到這個(gè)局部類(lèi)型invoker_type的定義了:

 

typedef typename get_function_invoker1<

          FunctionPtr,R,T0>::type invoker_type;

 

get_function_invoker1又是何物?很顯然,這是個(gè)traits,其內嵌的::type會(huì )根據不同的模板參數表現為不同的類(lèi)型,在本例中,::type的類(lèi)型將會(huì )被推導為

 

function_invoker1<int(*)(int),int,int>

 

function_invoker1是個(gè)類(lèi)模板,其定義為:

 

template<

typename FunctionPtr,

typename R,typename T0

> // 注意這里的模板參數,后面會(huì )解釋

struct function_invoker1

{

static R invoke(any_pointer function_ptr, T0 a0)

{

FunctionPtr f =

reinterpret_cast<FunctionPtr>(function_ptr.func_ptr);

return f(a0);

}

};

 

所以對invoker的賦值最終相當于:

 

invoker = &function_invoker1<int(*)(int),int,int>::invoke;

 

而從上面的function_invoker1的定義可以看出,

function_invoker1<int(*)(int),int,int>::invoke是一個(gè)靜態(tài)成員函數,它被實(shí)例化后相當于:

 

static int invoke(any_pointer function_ptr, int a0)

{

// 先轉型,再調用,注意,這一行語(yǔ)句還有一個(gè)額外的作用,在后面解釋

int (*f)(int) = reinterpret_cast<int(*)(int)>(function_ptr.func_ptr);

 

// 因為f指向的是用戶(hù)保存在該function中的函數或仿函數,

// 所以這一行語(yǔ)句進(jìn)行了最終真正的調用!

return f(a0);

}

 

我們可以看出,在invoke函數中,真正的調用現身了。

 

此外,如果用戶(hù)當初給出的是仿函數而不是函數指針,則有function_obj_invoker1與它對應,后者也是一個(gè)類(lèi)似的模板,它的invoke靜態(tài)成員函數的形式也是:

 

static R invoke(any_pointer function_obj_ptr, T0 a0);

 

其中function_obj_ptr是指向仿函數的指針,所以這個(gè)版本的invoke中對它的調用語(yǔ)句是這樣的:

 

FunctionObj* f = (FunctionObj*)(function_obj_ptr.obj_ptr);

return (*f)(a0); // 調用用戶(hù)當初給出的仿函數

 

最后一種可能:如果接受的是成員函數怎么辦呢?簡(jiǎn)單的答案是:boost::function并沒(méi)有為成員函數作任何特殊準備!理由也很簡(jiǎn)單,boost::function只要先將成員函數封裝為仿函數,然后將其作為一般的仿函數對待就行了,具體代碼就不列了,STL中有一個(gè)函數模板std::mem_fun就是用于封裝成員函數指針的,它返回的是一個(gè)仿函數。boost中也對該函數模板做了擴充,使它可以對付任意多個(gè)參數的成員函數。

 

做一個(gè),送一個(gè)——invoker的額外好處

我們注意到function的構造和賦值函數及其基類(lèi)的構造和賦值函數都是模板函數,這是因為用戶(hù)可能提供函數也可能提供仿函數,但最關(guān)鍵的還是,functiont提供一種能力:對于function<double(int)>類(lèi)型的泛型函數指針,用戶(hù)可以給它一個(gè)int(int)類(lèi)型的函數——是的,這是可行且安全的,因為其返回值類(lèi)型int可以安全的轉型為double,而對于這種類(lèi)型兼容性的檢查就在上面分析的invoke靜態(tài)成員函數中,這就是我們要說(shuō)的額外好處——如果類(lèi)型兼容,那么invoke函數就能正常編譯通過(guò),但如果用戶(hù)給出類(lèi)型不兼容的函數,就會(huì )得到一個(gè)錯誤,這個(gè)錯誤是在編譯器實(shí)例化invoke函數代碼的時(shí)候給出的,例如,用戶(hù)如果這樣寫(xiě):

 

// 聲明一個(gè)雙參的函數f

RT1 f(P1,P2);

 

  // 單參的function實(shí)例

function<RT(P)> f_ptr;

 

f_ptr = &f; // 類(lèi)型不兼容,錯誤!

 

這就會(huì )導致編譯錯誤,錯誤發(fā)生在invoke靜態(tài)成員函數中。下面我就為你解釋為什么。

 

我想你對function_invoker1的三個(gè)模板參數仍然心存疑惑,我們再一次來(lái)回顧一下其聲明:

 

template<

typename FunctionPtr,

typename R,typename T0>

struct function_invoker1

 

我們還得把目光投向assign_to模板函數,其中使用function_invoker1的時(shí)候是這樣的:

 

typedef typename ...::get_function_invoker1<

                         FunctionPtr,R,T0>::type invoker_type;

 

FunctionPtr,R,T0三個(gè)模板參數將會(huì )被原封不動(dòng)的傳給function_invoker1,那么對于我們上面的錯誤示例,這三個(gè)模板參數各是什么呢?

 

首先,我們很容易看出,FunctionPtr就是assign_to模板函數的模板參數,也就是用戶(hù)傳遞的函數或仿函數的類(lèi)型,在我們的錯誤示例中,函數f的類(lèi)型為RT1(P1,P2),所以

 

FunctionPtr = RT1(*)(P1,P2)

 

R,T0則是用戶(hù)在實(shí)例化function模板時(shí)給出的模板參數,我們寫(xiě)的是function<RT(P)>,于是:

 

R = RT

T0 = P

 

所以,對于我們的錯誤示例,invoker_type的類(lèi)型為:

 

function_invoker1< RT1(*)(P1,P2),RT,P>

 

對于這樣一個(gè)function_invoker1,其內部的invoke靜態(tài)成員函數被實(shí)例化為:

 

static RT invoke(any_pointer function_ptr,P a0)

{

  // 注意f的類(lèi)型

RT1 (*f)(P1,P2) = reinterpret_cast<RT1(*)(P1,P2)>(function_ptr.func_ptr);

   

return f(a0); //錯啦!瞧瞧f的類(lèi)型,f能接受一個(gè)P類(lèi)型的參數嗎?編譯器在此打住。

}

 

看看最后一行語(yǔ)句,所有的檢查都在那里了——我們最終把檢查委托給了C++底層的類(lèi)型系統。

 

很精妙不是嗎?雖然在模板形式的assign_to函數中,看起來(lái)我們并不關(guān)心到底用戶(hù)給的參數是何類(lèi)型,看起來(lái)用戶(hù)可以把任何函數或仿函數塞過(guò)來(lái),但是一旦下面觸及invoker的賦值,就得實(shí)例化invoke靜態(tài)成員函數,其中的:

 

return f(a0);

 

一下就把問(wèn)題暴露出來(lái)了!這種把類(lèi)型檢查延遲到最后,不得不進(jìn)行的時(shí)候,由C++底層的類(lèi)型系統來(lái)負責檢查的手法的確很奇妙——看起來(lái)我們沒(méi)有在assign_to函數中及時(shí)利用類(lèi)型信息進(jìn)行類(lèi)型檢查,但是我們卻并沒(méi)有喪失任何類(lèi)型安全性,一切最終都逃不過(guò)C++底層的類(lèi)型系統的考驗!

 

function如何對待成員函數

對于成員函數,assign_to的重載版本只有一行:

 

this->assign_to(mem_fn(f));

 

mem_fun(f)返回一個(gè)仿函數,它封裝了成員函數f,之后一切皆與仿函數無(wú)異。

 

關(guān)于mem_fun的細節,這里就不多說(shuō)了,大家可以參考STL中的實(shí)現,相信很容易看懂,這里只簡(jiǎn)單的提醒一下,成員函數封裝的效果是這樣的:

 

R (C::*)(T0,T1,...) => R (*)(C*,T0,T1,...) R (*)(C&,T0,T1,...)

 

safe_bool慣用手法

如你所知,對于函數指針fptr,我們可以這樣測試它:if(fptr) ...,所以function也應該提供這一特性,然而如果直接重載operator bool()則會(huì )導致下面的代碼成為合法的:

 

function<int(int)> f;

int b=f;

 

這顯然不妥,所以function用另一個(gè)巧妙的手法替代它,既所謂的safe_bool慣用手法,這在function定義內部的源碼如下:

 

struct dummy { void nonnull(){};};

typedef void (dummy::*safe_bool)(); // 確保safebool不能轉型為任何其它類(lèi)型!

operator safe_bool () const

{ return (this->empty())? 0 : &dummy::nonnull; }

 

這樣,當你寫(xiě)if(f)的時(shí)候,編譯器會(huì )找到operator safe_bool(),從而將f轉型為safe_bool,這是個(gè)指針類(lèi)型,if語(yǔ)句會(huì )正確判定它是否為空指針。而當你試圖把f賦給其它整型變量的時(shí)候則會(huì )遭到編譯期的拒絕——因為safe_bool是一個(gè)成員指針類(lèi)型,無(wú)法向其它整型轉換。

 

get_function_tag

get_function_tag用于萃取出函數所屬類(lèi)別(category),各個(gè)類(lèi)別在源代碼中已經(jīng)列出,至于它到底是如何萃取的,這與本文關(guān)系不是很大,有一點(diǎn)需要提醒一下:函數指針類(lèi)型也是指針類(lèi)型,這聽(tīng)起來(lái)完全是句廢話(huà),但是考慮這樣的代碼:

 

template<typename T> struct is_pointer{enum{value=0};};

template<typename T> struct is_pointer<T*>{enum{value=1};};

std::cout<<is_pointer<int(*)(int)>::value; // 這將輸出 1

 

也就是說(shuō)int(*)(int)可以與T*形式匹配,匹配時(shí)Tint(int)。

 

最后一些細節

1. 我沒(méi)有給出function_base的源代碼,實(shí)際上那很簡(jiǎn)單,它最主要的成員就是一個(gè)union any_pointer型的數據成員

 

    // 用于統一保存函數指針及仿函數對象指針

detail::function::any_pointer functor; 

 

2. 我沒(méi)有給出functor_manager的信息,實(shí)際上它與function的實(shí)現沒(méi)有太大關(guān)系,它負責copydelete函數對象,如果必要的話(huà)。所以我將它略去,它的源碼在”boost/function/function_base.hpp”里。

 

3. 我給出的源代碼是將宏展開(kāi)后的版本,實(shí)際的代碼中充斥著(zhù)讓人眼花繚亂的宏,關(guān)于那些宏則又是一個(gè)奇妙的世界。boost庫通過(guò)那些宏省去了許多可見(jiàn)代碼量。隨著(zhù)函數參數的不同,那些宏會(huì )擴展出function2,function3...各個(gè)版本。本文只研究了int(int)型的情況,其它只是參數數目的改變而已。經(jīng)過(guò)宏的擴展,function的偏特化版本將有:

 

template<typename R,typename Allocator>

class function<R(),Allocator>:public function0<R,Allocator>

{...};

template<typename R,typename T0,typename Allocator>

class function<R(T0),Allocator>:public function1<R,T0,Allocator>

{...};

template<typename R,typename T0,typename T1,typename Allocator>

class function<R(T0,T1),Allocator>:public function2<R,T0,T1,Allocator>

{...};

...

 

等更多版本,一共有BOOST_FUNCTION_MAX_ARGS+1個(gè)版本,BOOST_FUNCTION_MAX_ARGS為一個(gè)宏,控制最多能夠接受有多少個(gè)參數的函數及仿函數對象,你可以重新定義這個(gè)宏為一個(gè)新值,以控制function所能支持的函數參數個(gè)數的最大值。其中的function0,function1,function2等名字也由宏擴展出。

 

目錄(展開(kāi)boost源碼剖析》系列文章)

 

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
定義指向模板函數的函數指針類(lèi)型
三十分鐘掌握STL
C++ 模板基礎談 - C/C++ / C++ 語(yǔ)言
模板
C++
C++ Templates 筆記
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久