青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-90  評論-947  文章-0  trackbacks-0

目錄:

C++ 下 Function 對象的實現(上)
C++ 下 Function 對象的實現(下)

起因在上一篇已經說過了。現在讓我們直接進入主題。本文的目標是,讓以下代碼能順利跑起來:

int intfun0()
{
    return 1;
}

struct _intfunctor0
{
    int operator()()
    {
        return 2;
    }

} intfunctor0;

struct Test
{
    int intmem0()
    {
        return 3;
    }

} test;

int main()
{
    Function<int ()> f1(&intfun0);
    Function<int ()> f1_(intfun0);
    Function<int ()> f2(intfunctor0);
    Function<int ()> f3(&test, &Test::intmem0);

    f1();
    f1_();
    f2();
    f3();

    return 0;
}

除了上述例子中顯示的,還要支持有返回值的函數和沒返回值的函數,以及有0個、1個、2個、……、MAX 個參數的函數,參數類型無限制。最后實現的 Function 對象僅僅可以執行就好。(至于是否可拷貝、是否可判斷相等 等問題,都是小事,本文暫不考慮。)最后,Bind 概念也不在本文討論范圍之內。

對于這個問題,我們一開始考慮的可能是怎樣統一三種不同形式。有兩個選擇,第一,使用 C++ 的多態機制,最后統一到基類指針的類型;第二,允許類內部有冗余變量以及必要的 Flag,用于判斷是哪種形式的函數,要如何執行。這樣看起來,第一種方案比第二種爽一點。于是,最初想到的實現有可能是這樣的:

先定義一個虛基類:

template <typename R>
class FunctionBase0
{
public:
    virtual R Invoke() = 0;
    virtual ~FunctionBase0() {}
};

然后實現一個普通函數/仿函數的版本:

template <typename R, typename T>
class Function0 : public FunctionBase0<R>
{
public:
    R Invoke()
    {
        return m_Fun();
    }

public:
    Function0(const T &fun)
        : m_Fun(fun)
    {

    }

private:
    T m_Fun;
};

這里需要說明的是,如果是普通函數,T會被特化成 R() 或者 R (&)() 或者 R(*)(),取決于使用的時候傳入 fun 還是傳入 &fun。所以不必另外實現針對 R(*)() 的版本。Loki (姑且就以作品名稱乎 Loki 的作者吧,他那個真名實在是太長)在他的書中稱之為“做一個,送一個”。不過對于他書中所說的,我有一個疑惑。Loki 說傳入 fun,模版參數 T 會被特化成 R (&)(),于是一切順利。可是我在操作過程中發現 T 一直被特化成 R (),于是上述 class 中的 m_Fun 被認為是成員函數而不是成員變量。不知道是為什么,有知道者請不吝指教哈。因為以上原因,本文中我一直用 &fun 的形式對待普通函數。

再實現一個成員函數的版本:

template <typename R, typename T>
class MemberFunction0 : public FunctionBase0<R>
{
public:
    R Invoke()
    {
        return (m_pObj->*m_pMemFun)();
    }

public:
    MemberFunction0(T *pObj, R (T::*pMemFun)())
        : m_pObj(pObj), m_pMemFun(pMemFun)
    {

    }

private:
    R (T::*m_pMemFun)();
    T *m_pObj;
};

最后是一個包裝類。如果你可以接受 Function<int> 表示 int(), Function<int, int> 表示 int (int),…,那么這里沒有多少技巧可言。boost 的那個 function 使用的是函數簽名作為模版參數,即 Function<int()>,Function<int (int)> 等形式。如果不太研究語法,可能會像我一樣,一開始會對尖括號里的 int (int) 之類的玩意兒不太熟悉,覺得很牛逼。可是了解了以后,不過是個函數類型而已,沒什么大不了的。Loki 的 Functor 的使用方式是 Functor<int, TYPELIST_0()>,Functor<int, TYPELIST_1(int)>。其中第一個模版參數始終是返回值,第二個模版參數是參數類型列表,Loki 使用了他創造的玩意兒 TypeList 使得所有函數參數只占一個坑,這在等下的支持多參數的擴展中能夠帶來一些美觀。我比較喜歡 boost 的使用方式,讓使用者直接以語言規定的形式填入函數簽名,而不是一些額外的約定(“第一個模版參數表示返回值”,“第二個到最后的模版參數表示參數”,“第二個模版參數以 TypeList 形式表示函數參數”等)。

為了達到這個目標,我們要玩一些偏特化技巧。關于偏特化,我一直以來的膚淺認識都是錯誤的。我原以為,對于模版類:

template <typename T0, typename T1>
class Foo;

我如果特化其中一個參數 T1:

template <typename T0>
class Foo<T0, int>
{

}

我以為只有這樣才叫偏特化,以為偏特化的過程總是減少模版參數的。而實際上,只要用某個/些類型占據原始模版參數的位置,就可以了。比如,對于上述 Foo,我可以特化一個 class<T0, std::map<U0, U1>>,消去一個 T1,而新增 U0、U1:

template <typename T0, typename U0, typename U1>
class Foo<T0, std::map<U0, U1>>
{

}

原來 T1 的位置被 std::map<U0, U1> 占據了,這也是偏特化。當然最后的模版參數數量也可以不變,如:

template <typename T0, typename U>
class Foo<T0, std::vector<U>>
{

}

以及

template <typename T0, typename U>
class Foo<T0, U*>
{

}

其中后者是實現類型萃取的主要方式。只要特化以后,這個類依然帶有至少一個模版參數,就是偏特化。如果最后產生了 template<> 的形式,那就是完全特化。

回到我們剛才的主題,我們要提供給用戶的是這樣一個類:

template <typename Signature>
class Function;

其中參數 Signature 會被實際的函數類型所特化。但是我們只知道整體的一個 Signature 并沒有用,我們必須知道被分解開來的返回值類型、參數類型。于是,引入一個偏特化版本:

template <typename R>
class Function<R ()>

這里使用 R () 特化原始的 Signature,引入一個新的參數 R。于是返回值類型 R 就被萃取出來了。實現如下:

template <typename R>
class Function<R ()>
{
public:
    template <typename T>
    Function(const T &fun)
        : m_pFunBase(new Function0<R, T>(fun))
    {
       
    }

    template <typename T>
    Function(T *pObj, R (T::*pMemFun)())
        : m_pFunBase(new MemberFunction0<R, T>(pObj, pMemFun))
    {

    }

    ~Function()
    {
        delete m_pFunBase;
    }

    R operator ()()
    {
        return m_pFunBase->Invoke();
    }

private:
    FunctionBase0<R> *m_pFunBase;
};

如果對上面說的“普通函數的使用方式必須是函數指針而不是函數本身”耿耿于懷,可以再引入一個的構造函數:

typedef R (FunctionType)();

Function(const FunctionType &fun)
    : m_pFunBase(new Function0<R, FunctionType &>(fun))
{

}

這里 FunctionType 是 R(&)() 類型,強制使用它來特化 Function0 中的 T。該構造函數在重載決議中會取得優先權從而使普通函數本身的傳入成為可能。不過,以函數本身形式傳入的普通函數會喪失一些特性,比如 Function<int()> 只能接受 int() 類型的普通函數而不能接受 char () 型的普通函數,因為這種情況下不會走我們剛才新定義的構造函數。

還有一種做法,就是針對全局函數,強制特化出模版參數為其引用類型的類。定義如下元函數:

template <typename Signature>
struct FunctionTraits
{
    typedef Signature ParamType;
};
   
template <typename RetType>
struct FunctionTraits<RetType ()>
{
    typedef RetType (&ParamType)();
};

然后構造函數改為:

    template <typename T>
    Function(const T &fun)
        : m_pFunBase(new Function0<R, typename FunctionTraits<T>::ParamType>(fun))
    {
       
    }

用以上方法,所有的特性都不會丟失。

到這兒,我們的 Function 已經可以小試牛刀了:

Function<int ()> f1(&intfun0);

Function<int ()> f1_(intfun0);
Function<int ()> f2(intfunctor0);
Function<int ()> f3(&test, &Test::intmem0);

f1();
f1_();
f2();
f3();

上面這段代碼已經能夠正常運行了。

來,繼續做一個,送一個。下面的代碼居然也能跑(voidfun0、voidfunctor0、Test::voidmem0類似int版本定義):

Function<void ()> f4(&voidfun0);
Function<void ()> f4_(voidfun0);
Function<void ()> f5(voidfunctor0);
Function<void ()> f6(&test, &Test::voidmem0);

f4();
f4_();
f5();
f6();

這說明了,在類里面寫一個返回值為該類型的函數,并在里面寫下 return XXX; 然后以 void 為模版參數傳入該模版類,是符合語法的。驗證一下:

template <typename T>
class Foo
{
public:
    T Bar()
    {
        printf("%s invoked\n", __FUNCTION__);
        return T();
    }
};

int main()
{
    Foo<void> f1;
    f1.Bar();

    Foo<int> f2;
    int i = f2.Bar();

    return 0;
}

運行結果:

Foo<void>::Bar invoked
Foo<int>::Bar invoked

到此為止,我們已經實現了 0 個參數的函數支持,也即 R () 類型的所有函數的支持。接下來還要實現對具有 1 個、2 個、3 個直至任意有限個參數的函數支持。也許您也發現了,接下來的工作可以是體力活,我們可以照葫蘆畫瓢,搞出一堆 FunctionBaseN、FunctionN、MemberFunctionN,并在最后的 Function 中再實現 N 個偏特化版本。是,不錯,大致上原理就是這樣。限于篇幅,我想暫時寫到這里,下篇將繼續談談宏、TypeList,以及怎樣少花點力氣實現其余 N 個版本。最終達到的效果是,只要改一個宏定義,就可以提高參數上限。

在本文所涉及的內容中,我比較糾結的是,可否在不用多態機制的情況下達到比較優雅的形式統一?

歡迎討論。

posted on 2011-01-16 22:17 溪流 閱讀(7345) 評論(55)  編輯 收藏 引用 所屬分類: C++

評論:
# re: C++ 下 Function 對象的實現(上) 2011-01-16 22:32 | zhaoyg
幫頂  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-16 22:43 | cexer
不說MFC那種陳舊的消息機制,用boost::function,boost::bind,boost::signal之類的來實現消息機制,也算有點out了,而且這種實現已經有框架使用(比如SmartWin++),不再具有新意了,其實強大的C++可以完成遠比它們更高級的功能,列舉三點:

1 框架可以給同一個消息源添加任意多個個完全不同的消息處理器,類似函數重載,可以將這個功能命名為處理器重載。僅僅是類似boost:function和boost:signal那種固定簽名的工具,是無法完成這樣的功能的,如下所示:

// 自由函數
void global_handler(){}

// 一段腳本
Script script_handler = create_script( /*這是一段腳本*/ );

// 系統函數
int WINAPI MessageBox( HWND,LPCTSTR,LPCTSTR,UINT );

// 具有不同參數的成員函數
viod Class:memeber_handler_void(){}
void Class::member_handler_size( Size size ){}
void Class::member_handler_int( int x,int y ){}

// 消息映射
Window window;
Class object;
window.on_resized += message_handler( global_handler );
window.on_resized += message_handler( script_handler );
window.on_resized += message_handler( &Class::member_handler_void,&object );
window.on_resized += message_handler( &Class::member_handler_size,&object );
window.on_resized += message_handler( &Class::member_handler_int ,&object );
window.on_resized += message_handler( &::MessageBox,NULL,"Hello World",MB_OK );


2 框架能夠進行返回值檢查(眾所周知的,在windows消息系統中,很多消息需要進行特殊的返回值檢查,強WM_ERASEBKGND之類的),比如一個closing消息,如果處理器返回非void類型,并且能夠強制轉換為true,則表明它同意關閉窗口,如果能夠強制轉換為false,則表示它阻止框架進行關閉,如果處理器返回值類型是void,則表示它不關心關閉與否,讓框架進行默認處理。這個功能可以命名為返回值智能檢測。如下所示:

// 這個處理器不關心窗口是否要關閉
void handle_closing_dont_care( Window* window )
{
}

// 這個處理器告訴框架不要關閉
LRESULT handle_closing_no()
{
return 0;
}

// 這個處理器告訴框架進行關閉
bool handle_closing_yes()
{
return true;
}

// 消息映射,窗口最終是否關閉取決于最后一個處理器的決定。
Window window
window.on_closing += message_handler( handle_closing_dont_care );
window.on_closing += message_handler( handle_closing_no );
window.on_closing += message_handler( handle_closing_yes );


3 框架提供消息分解者,允許使用者自己組合自己的處理器參數,類似boost::asio的handler綁定bytes_transfered和error_code的方式,可以把這個功能命名為消息參數任意組合,比如同一個消息resized,有的處理器需要當前窗口的大小,有的處理器需要當前窗口的標題,有的處理器同時需要兩者,則消息映射時手動組合消息分解者即可。更進一步,對于某些預注冊的簽名,框架可以智能提供分解者,免去使用者手動提供的麻煩,例如簽名 (Window*,Size) 已經預先注冊,則具有這個簽名的處理器不用手動指定消息分解器
示例代碼如下:

// 這個處理器需要當前窗口大小
void handle_resized_size( Size size ){}

// 這個處理器需要窗口標題
void handle_resized_text( String caption ){}

// 這個處理器都需要
void handle_resized_both( Size size,String caption )


// 消息映射的時候,同時指示自己需要什么參數
Window window
window.on_resized += message_handler( handle_resized_size,Window::Argument::size )
window.on_resized += message_handler( handle_resized_text,Window::Argument::text )
window.on_resized += message_handler( handle_resized_both,Window::Argument::size,Window::Argument::text )


void handle_resized( Window* window,Size size ){}
window.on_resized += message_handler( handle_resized );

4 但是。。。。。在C++.0x.lamda的面前,以上一切都成了浮云。把lamda作為消息處理器,它自身就同時提供了以上1,2,3的所有優點,簡潔優雅使用方便,物美價廉童叟無欺,以上例說明:

// 這個處理器需要當前窗口大小
window.on_resized += []( Size size ){
}( window::Argument::size );

// 這個處理器需要窗口標題
window.on_resized += []( String caption ){
}( Window::Argument::text );

// 這個處理器都需要
window.on_resized += []( Size size,String caption ){
}( window::Argument::size,Window::Argument::text );

我正在寫的就是這么一個框架,上面列舉的消息機制都完成了,正在進行博主所指第4步封裝控件和第5步(寫文檔,作為一個目標是開源的框架,是最重要的步驟),目前代碼已經有兩萬多行,消息機制的其實占很少部分,大多代碼是控件的封裝。

  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-17 12:37 | 飛舞的煙灰缸
@cexer
function的多播實現還是有點麻煩。
+= 倒是簡單,但是要實現 -=這樣的操作就需要判斷function相等,麻煩就比較大了  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-17 12:52 | 溪流
@飛舞的煙灰缸
求不借助于typeid的function相等的判別方法~  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-17 12:53 | 溪流
@cexer
很有啟發性的提示,謝謝分享~!頂了再細讀。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 02:01 | OwnWaterloo
typelist 不給力的, 它沒辦法展開為一個參數列表。
最終還是需要boost.pp 那樣的東西。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 02:17 | OwnWaterloo
借寶地請教一下~ 對于handler, 是否需要如此的動態?

A. 完全動態

比如, 一個具體的 window 類。
window w; // 具體的一個類
w.add_handler( ON_CREATE, my_on_create, ... ); // 動態的添加一些handler。

具體語法不重要, w.add_handler<ON_CREATE>(my_on_create, ...);
關鍵點是, window是一個具體類, 動態的(運行時而非編譯時)添加一些handler。

if (some_condition)
/*run time*/w.add_handler( ON_CREATE, my_on_create, ... );


B. 完全靜態

與之相對的, 是編譯時就決定一個類型, 比如 my_window。
然后讓它處理 ON_CREATE。 如何讓WndProc分派到這個handler同樣不是重點。
重點是my_window的每個instance肯定都會處理這個event, 沒有例外。


C. 兩者結合, 動態部分自己實現。

兩者的折中可能是這樣:
1. 依然是一個具體的, 編譯時決定處理哪些event的window類, 比如叫y_window
2. 在設計y_window時, 認為有"部分"(而非window那樣的全部)行為需要在運行時定制
3. 在event中留一些stub, 用于運行時添加。

比如 y_window::on_create( param ){
/*同樣, wndproc 如何分派到這里不重要*/
/**/for (it=this->on_create_handlers.begin(); it!=this->on_create_handlers.end(); ++it) *it(param);
}
y_window::add_on_create( f , ... ) {
/**/this->on_create_handlers.push_back(f, ... );
}


如上, 需要A那種靈活性么? 缺之不可?
因為我自己確實沒寫過幾個gui程序, "作為一個庫的用戶", 沒有足夠的這方面的使用經驗。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 11:47 | yrj
@OwnWaterloo
動態方式解耦庫的實現與庫的用戶,可看看下面這篇文章
http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 16:53 | zhaoyg
"可是我在操作過程中發現 T 一直被特化成 R (),于是上述 class 中的 m_Fun 被認為是成員函數而不是成員變量"

這個會不會是編譯器理解成你試圖在定義一個函數的變量,于是才...


  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 16:56 | zhaoyg
如果 m_Fun是T* 而不是T , 可能就沒有這個問題了  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 17:28 | cexer
@OwnWaterloo
【借寶地請教一下~ 對于handler, 是否需要如此的動態?
A. 完全動態
B. 完全靜態
C. 兩者結合, 動態部分自己實現。
如上, 需要A那種靈活性么? 缺之不可?
因為我自己確實沒寫過幾個gui程序, "作為一個庫的用戶", 沒有足夠的這方面的使用經驗。】


說說我的看法:A并不是必須的,比如說MFC,可以算是完全靜態的,其所有的消息映射代碼都是用宏堆死的選擇語句,提供的動態功能基本上無,優勢很明顯,一個類的所有實例共享一份映射數據,具有空間優勢。但是一個獨立的對象不能有它自己處理事件的方式,這明顯不是面向對象的封裝方式,運行時靈活性大打折扣,這樣說起來很模糊,仍然舉例MFC來說明:

比如說使用MFC運行時創建的一個按鈕,怎么樣才能把這個按鈕的消息加到消息映射里去呢?對不起,不能。因為按鈕ID是動態生成的,那些映射宏什么的到運行時都成了浮云。解決辦法當然也有:回到原始時代--要么在父窗口的消息回調函數里檢查WM_COMMAND和BN_CLICK,二是任勞任怨地去自己重寫一個按鈕類,并在自己的消息回調里響應WM_COMMAND的反射消息并檢測BN_CLICK,然后進行處理--看看有多麻煩。

所以我覺得一個好的庫,應該能夠同時提供兩者,就是你說的C。同時支持靜態和動態的映射方式,關鍵是庫要實現得好,在沒有使用動態映射的時候,這個動態映射的功能就好像不存在一樣,這樣大多數時候既能夠享受空間優勢,在需要的時候又能發揮它的動態優勢。免得用戶要在在空間優勢和靈活性之間做出痛苦的選擇。
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 17:38 | cexer
@飛舞的煙灰缸
【function的多播實現還是有點麻煩。
+= 倒是簡單,但是要實現 -=這樣的操作就需要判斷function相等,麻煩就比較大了】

我想你指的麻煩,可能是你想實現這樣的功能:
// 加入
on_event += message_handler( handler );
// 刪除
on_event -= message_handler( handler );

// 加入
on_event += message_handler ( &Window::handle_resized,this );
// 刪除
on_event -= message_handler ( &Window::handle_resized,this );

這樣的方式實現其實也不難,但是絕對需要 RTTI 那種東西的支持,另外還有一個問題就是這樣的方式使用用起來太麻煩,比如:

// 加入
on_event += message_handler ( &Window::handle_resized,this );
// 刪除
on_event -= message_handler ( &Window::handle_resized,this );

如果庫提供了類似 boost::asio 那種參數綁定的功能,看看有多麻煩:
// 加入
on_event += message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );
// 刪除
on_event -= message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );


其實這個功能有比你個簡單得多的實現方式,給一個 handler 綁定一個名字,到時刪除這個名字即可,見下例:
// 加入
on_event += message_handler ( name,handler );
// 刪除
on_event -= message_handler( name );


  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 17:59 | cexer
實現一個functor,除了眾所周知的功能,我建議樓主再考慮以下這兩點:

1 給返回void(無返回值)的functor綁定一個有返回值的可調用體,這個貌似boost::function是支持的。這功能是需要的,因為實際上很多時候,我們的functor不關心返回值,就好像某個消息無論你返回什么系統都執行相同的操作,這種情況下,如果綁定的可調用體返回了什么值,functor應該檢測到并使用某種屏蔽手段,而不是報錯“對不起,返回類似不匹配”。

2 給返回非void(有返回值)的functor綁定一個返回void(無返回值)的可調用體,這個貌似boost::function不支持。這功能是需要的,因為有時候functor需要一個明確的返回值,就像WM_ERASEBKGND消息,根據返回TRUE或FALSE的不同,系統會決定是否自己來畫窗口背景。如果可調用體返回void(無返回值),那么functor應該檢測到它,并在返回時提供一個默認的返回值,而不是在綁定時就報錯“對不起,返回類似不匹配”。

以上兩點都可以通過模板元編程實現,通過typetraits檢測返回值,并根據functor和可調用體之兩者返回值的不同組合,來選擇不同的返回策略。

另外還有,如果想綁定系統函數如Windows API,或者其它第三方提供的函數,你還需要考慮調用約定的問題,因為 __stdcall,__cdecl,__fastcall的不同,都會使函數簽名不同。有多少種調用約定,functor的綁定函數的數量就需要乘以多少,這是個體力活,可以用預處理元和文件包含來減少體力消耗。
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 18:27 | OwnWaterloo
@cexer
感謝感謝~ 果然是經驗豐富的大神。

我想說的就是對象模型不同導致的"空間和效率"上的差異。
C++ 其實不是"面向對象"語言, 而是"面向類" 語言。
真正的面向對象語言, 每個instance都是相互獨立的。
而C++,每一個type的所屬instance共享一部分行為。


C++其實就是將"可能擁有一組相同行為"的instance們, 歸屬到同一個類里,
共享許多信息, 并提高空間與時間效率。
但是, 當instance們的行為復雜(靈活)到一定層次時, 極端情況, 就是一個class, 只需要一個instance。


這時候, 如果依然遵守靜態類型的桎梏, 為每種行為組合編寫一個類, 就會非常繁瑣, 遠不如動態類型語言來得方便。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 18:37 | OwnWaterloo
@cexer
上面所說的"一個類只需要產生一個instance"是非常極端的情況。
實際上是否真的會產生這樣的情況?


比如那個動態產生button的問題。
如果依然遵守靜態類型的方式, 既然"預見" 會產生多個button, 是否可以先在on_command里留下stub, 然后把on_command這部分動態化。
每個button被按下的行為不同, 但這些行為是否依然是"可預見"的?
是否依然可以將它們歸入到同一類型中, 比如使用類多態機制?


"A完全動態" 的方案給我的感覺是: 這已經不是在用C++, 而是在C++中實現一個微型動態類型系統, 然后用動態類型的方式編程。
如果是我個人作出選擇的話, 當需要這種靈活性時, 直接把C++丟了, 選其他動態類型語言來編程-_-  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 18:39 | 溪流
@zhaoyg
是的,T*自然沒問題,就是為了同時也支持傳入 T。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 18:51 | OwnWaterloo
@cexer
>> 同時支持靜態和動態的映射方式,關鍵是庫要實現得好,在沒有使用動態映射的時候,這個動態映射的功能就好像不存在一樣,這樣大多數時候既能夠享受空間優勢,在需要的時候又能發揮它的動態優勢。免得用戶要在在空間優勢和靈活性之間做出痛苦的選擇。


說得我口水都流了…… (其實是開飯了~)
有沒有機會能先睹為快呢~


假設, 這部分能做到上述所說。
但還是有別的問題。

比如參數綁定(boost.bind), 在其他范疇里叫partial apply。
其他語言要實現這個簡直太容易了, 語言直接支持, 不需要一個額外的庫。
也不需要關心被bind的參數的生命周期問題。

比如匿名函數, boost.lambda應該也是有不少限制的, C++0x目前也不算太成熟 —— 還有這么多人用VC6呢…… 能讓他們進化到VC8就很感謝了, VC10感覺是奢望。


還有你上面提到"給handler一個名字, 以方便從handler們中刪除" —— 這完全就是動態類型語言常用的方式嘛……
比如lua:
handlers = {}
添加:
handlers[name] = handler
調用:
for _,v in pairs(handlers) do v( ... ) end
刪除:
handlers[name] = nil


還有樓主提到的typelist, 其實是無法展開為參數列表的。
這功能在動態類型語言里也太容易了……
最簡單的元編程方式, 就是產生所需代碼的字符串, 然后加載之……


總之呢, 當需要靈活性時, C++就缺這缺那的, 什么都要自己去造……
造的時候, 又有各種問題。


造的時候肯定是很有成就感的, 編程的樂趣啊!
但就我個人而言, 已經懶惰了……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 18:58 | OwnWaterloo
@cexer
來說一個具體的C++問題吧, 就是這個:
>> 你還需要考慮調用約定的問題,因為 __stdcall,__cdecl,__fastcall的不同,都會使函數簽名不同。有多少種調用約定,functor的綁定函數的數量就需要乘以多少,這是個體力活,可以用預處理元和文件包含來減少體力消耗。

絕對的體力活啊! 干過一次絕對不想干第二次……

而且, 你有試過g++么?
g++中這問題很麻煩。
函數重載目前是通過mangling實現的, 而g++對一個函數(free or member)指針類型的mangling只包含普通的signature, 不包含調用約定。

偽代碼:
binder bind( __cdecl f, arg )
binder bind( __stdcall f, arg )

倆f在g++中是不同類型, boost::is_same是false。
但這倆bind函數"不構成重載" , 是bind的重復定義……
原因就是上面提到的, f被mangling時, 它的調用約定信息被忽略了。
倆bind最終產生的符號是相同的。


用C++, 就要和這些許多細枝末節的問題作斗爭……
懶了…… 想逃避之……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 19:24 | OwnWaterloo
@cexer
同時, 我還發現C++程序員很容易產生一種傾向:
嗯, 我要設計一個程序庫, 這個庫很smart, 無論怎么使用, 都是可以的。

想怎么用, 就怎么用 —— 以前我也是這樣想的。


就舉這個帖里出現的一個例子: 某處理器需要一個返回值。 是否可以讓程序員在需要默認返回時, 編寫一個返回void的處理器?

就我個人而言 ( 僅僅是個人的看法, 與技術無關了, 兩者都是對的), 類似WTL那樣也是"完全可用"的。
... frame work ...
case WM_XXX :
R result = default_result;
user_handler(&result, ... );
return result;

簽名必須寫為:
(R* response, ... ); // 其實是個引用, 只是指針的話, 上面的frame work中的代碼意圖更明顯。

"有且僅有這一種做法" , 沒有第二選項。
若用戶不關心, 不去修改response的值即可。


其實我也覺得傳出參數是很難看的設計。
但它可工作, 也可以避免去編寫一堆meta programming 代碼, 而且這堆代碼可能又需要同C++的各種陰暗作斗爭。
例如boost, 若不需要"照顧" 這么多編譯器的話, 其實代碼還是可讀的。
但需要照顧這么多編譯器, 那無數的workaround, 使得代碼變得……
不是天書也是爛泥……



"嗯, T是一個原始指針可以, 當它是個smart pointer時也應該要被支持"。
很多時候, C++ 程序員不是去考慮這種需求是否存在, 實現這種需求的復雜度如何, 維護代價如何。
僅僅是因為他們能, 所以他們就要這樣做……

其實有時候, 這些需求僅僅是美學上的, 而不是技術上的。
比如上面那個返回值的例子。
再比如:

f(char const* s, ... ); // primary function
f(std::string const& s, ... ) { return f(s.c_str(), ...); }
f(CString const& s, ... ) { return f(static_cast<char const*>(s), ...); }
...

如果不要后兩者, 任何工作依然可以完成。
后兩者僅僅是提供一種方便, 而不是"核心"接口。

也不是說這種方便不好…… 我的觀點是一定需要有某種方式告之用戶, 哪部分是核心接口。
用戶優先去掌握這部分, 然后整個庫的功能他就可以完全掌握了。
其他的, 學得越多, 使用越方便。

否則, 若沒有這種通告, 一個f不要緊, 若全體函數都這樣搞, 就會增加學習成本。
本來15個核心函數, 如果擴展為45個, 75個, 就不僅僅是量上的變化, 而是質上的變化 —— 讓人失去耐心。
這同為什么要抑制warning一樣, 不是說不可以產生warning, 而是說當warning多到一定程度時, 會超過人的處理極限, 會不自覺的將他們全體忽略掉。
所以發現warning就要干掉, 讓新的warning能引起人注意。


至于通告方式…… 感覺手寫文檔是最靠譜的……
docxgen 什么的…… 產生的文檔太死氣沉沉, 無法感受到當中的輕重緩急。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 19:37 | OwnWaterloo
舉個實際例子 (我就一話癆……) :
最近在教人C語言……

條件包含部分: #if, #elif, #else, #endif, defined 就完全ok了。
所有功能都可以實現。
但C語言還提供了 #ifdef, #ifndef…… 而且人們通常用的就是這個方便的接口……
而且很多時候連#elif, #else都沒有 —— 比如頭文件保護符, 我就是在給別人解釋這個事情。

為什么有#ifdef, #ifndef后, 還要有 #if 與 defined?
若不給初學者解釋 #elif , 是肯定理解不了的。


如果C語言一開始就只為條件包含提供一種方法: #if, #elif, #else, #endif, defined。
大家也就只能這么寫。
那介紹頭文件保護符時, 就先介紹#if, defined, #endif就完了, 暫時不需要牽扯出其他部分, 其他部分可以慢慢了解。
人腦沒法一下子容納太多信息的。



再聲明一下, 以上都是個人觀點, 話癆犯了, 聊聊天而已。
完全沒有"其他人也必須這樣", "這就是事實"的意思……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 19:39 | OwnWaterloo
@yrj
感謝~
其實我困惑的不是"如何實現", 而是"是否需要在C++中實現"。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-18 23:49 | 溪流
@cexer
@OwnWaterloo
關于調用約定,貌似不用復制N份(VS2010下測試),函數會自動被識別為 R (__stdcall *)(...)  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:00 | 溪流
@OwnWaterloo
很多時候, C++ 程序員不是去考慮這種需求是否存在, 實現這種需求的復雜度如何, 維護代價如何。
僅僅是因為他們能, 所以他們就要這樣做……
其實有時候, 這些需求僅僅是美學上的, 而不是技術上的。

“僅僅是因為他們能, 所以他們就要這樣做……”有時候確實這樣,但一般實現代價一般很小的情況下才會出現這種沖動,想要順手做了。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:17 | 溪流
@cexer
謝謝分享這么多想法。

給 handler 起名字確是刪除 handler 的好辦法,我可以不用糾結要不要用 typeid 啦。

另外關于你說的不同類型的handler的綁定,作為 Function,我覺得不該加入這樣的特性;作為GUI專用的Event,是可以接受有的特性的。

但是另一點疑問是,框架告訴用戶的規則——“返回 void 表示讓框架決定是否繼續處理,返回bool表示用戶想讓框架繼續處理或者拒絕用戶繼續處理”,與WTL的bHandled告訴用戶的——“設置bHandled用于指示框架是否繼續處理”相比,優勢大嗎?都存在一種約定。而用返回void與bool的形式,可能僅僅是更加炫一點?

實現上,不管用戶看到的參數是怎樣的,框架最初獲得的肯定是 WPARAM 和 LPARAM,這樣,就算不修改 Function,似乎也可以在控件中給出不同的處理函數形式,例如給出一組重載的 OnClick:
OnClick(Function<void (int, int)>)
OnClick(Function<bool (int, int)>)
又例如給ClickEvent實現重載的operator+=?

前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅定了想法,解析之!。。讓使用者不需要查 MSDN。。。或許做不到這種程度吧。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:22 | OwnWaterloo
@溪流
貌似你誤會了。

cexer所說的應該是這個情況:
比如需要實現bind(或者是別的功能, 這個不是重點, 只是用于舉例)。
重點是: 庫的用戶會用不同的調用約定的函數傳入。

還是上面的偽代碼:
binder bind( f , arg ) { ... }

R0 __cdecl F0_of_client ( ... );
R1 __stdcall F1_of_client ( ... );

bind(F0_of_client, ... );
bind(F1_of_client, ... );

只有兩者之一會成功。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:24 | 溪流
@OwnWaterloo
歡迎聊天~~~

warning神馬的,感覺就是要見一次殺一次,不然絕對破窗效應。

“當需要這種靈活性時, 直接把C++丟了,選其他動態類型語言來編程。”有一群糾結的人(當然不是我),他們的信仰就是用 C++ 做任何事,或者說他們的興趣就是用C++ 來做任何事,那怎么辦呢?呵呵。且不說個人興趣導向。從很理性的結果主義的角度考慮,這種時候確實得換,應該要換,但也不是每個這樣的場合都有條件換,比如團隊里沒有會python的且不想學python,但有一群蛋疼的Cpper,這種事情也會發生吧。

最后,你說你在教C?求教,求升華~!·^_^  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:25 | OwnWaterloo
@溪流
>> 但一般實現代價一般很小的情況下才會出現這種沖動,想要順手做了。

以前是不管代價如何就很沖動去順手了……
現在想來……
也算是練手吧……
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:29 | OwnWaterloo
@溪流
>> 前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅定了想法,解析之!

解析WPARAM和LPARAM確實無比無比蛋疼啊……
當然, 最初的那些C程序員也不是沒辦法了, 有個 windowx.h 里面的宏有一些幫助。

我也覺得, 作為一個有責任的庫, 應該做之。
如果能提供沒有解析的原始層次, 供那些有經驗的程序員使用, 似乎也可以?


似乎程序員都有實現gui的沖動?
我也沖動過的…… 但就是被WPARAM, LPARAM擊敗了……
本想以此當作熟悉各種消息的方式, 最終還是沒堅持下來……
沒毅力的人飄過……
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:31 | OwnWaterloo
@溪流
嗯嗯, 這個確實符合破窗效應。

但15個api與45、 75個api應該用什么效應解釋呢?
反正就我的感覺, 15個會很有耐心的去看; 而45個就沒耐心了; 75個就絕對不想去看了, 并給自己一個心理安慰:這玩意太復雜, 沒設計好……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:33 | 溪流
@OwnWaterloo
bind的時候會遇到怎樣的情形我目前不知道。但是就 function 的階段來說,我剛剛自認為比較仔細地確認了代碼能夠跑的樣子:

void __cdecl f1(int)
{

}

void __stdcall f2(int)
{

}

int main()
{
Function<void (int)> g1(&f1);
Function<void (int)> g2(&f2);
g1(0);
g2(0);

return 0;
}

觀察匯編結果,f1的ret出來后有 add esp 4動作,f2的ret 4出來后沒有  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:34 | OwnWaterloo
@溪流
嗯, 理想是一回事。 有時候還是得向現實屈服……
作為個人的話, 還是可以學學python(perl, ruby) 什么的, 日常編程工作就可以擺脫C/C++了……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:35 | OwnWaterloo
@溪流
你會C++還怕C么……

說是教, 也就是以前的死黨指點指點。
都是灰常基礎的東西……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:47 | OwnWaterloo
@溪流

template<typename R>
R call( R (*f)() ) { return f(); }

void __cdecl f() { printf("cdecl\n"); }
void __stdcall g() { printf("stdcall\n"); }

VC8默認情況: call(f) 是可以的, 而 call(g)不行。
因為 call 中的 f 參數沒有指定調用約定。
如果顯式使用 /Gr , 那就call(f)不行, call(g) 可以。



如果使用重載:
template<typename R>
R call( R ( __cdecl *f)() ) { return f(); }


template<typename R>
R call( R ( __stdcall *f)() ) { return f(); }

兩者都可以, 但在g++下, 只要兩者被同時使用, 就是重定義。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:50 | 溪流
@OwnWaterloo
多學點自然是應該的,但我覺得不應該追求日常編程脫離C/C++啊。。。那是件多么可怕的事,關系到飯碗。當然不是歧視腳本語言什么的,但是從現狀看來,越來越多的人在用更加簡單的語言,業界最稀有的的還是C/C++的人啊。(vczh說的生存斗爭觀點我很贊同。)越簡單意味著入門越容易,競爭者多,于是想勝出就難了。所以我覺得往復雜方面靠的大方向還是要的。所以。。。用API不如造API,用輪子不如造輪子,不如用造輪子的輪子。。。臨了的時候,也許可以居高臨下的跟別人說:你,不就是會很多API么?這些API我都造過。你,不就是會用很多輪子嗎?這些輪子我都造過。你,不就是會用很多語言嗎?這些語言我也都造過。。。哈哈,扯淡了  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:55 | OwnWaterloo
@溪流
誤會誤會…… 我說的"日常"是指工作飯碗以外的……

比如, 用metablog 轉移blog文章, 好像就是你寫過的吧?
這用C/C++就蛋疼了……

就是一些個人事務, 有重復與機械的味道, 而用C/C++去做太麻煩。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:56 | 溪流
@OwnWaterloo
但是只要不要把返回值拆出來,就是可以的

template<typename F>
void call(F f ) { return f(); }

void __cdecl f() { printf("cdecl\n"); }
void __stdcall g() { printf("stdcall\n"); }
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 00:59 | 溪流
@OwnWaterloo
呵呵,那玩意兒我本想改成一個博客客戶端,一直沒動。。。
可是,工作范圍以外,就更要玩感興趣的語言了呀~
我寫轉移文章的東西,不是為了轉移文章,是為了寫。
當然,碰到一些具有真實需求的并且有緊迫性的事情,那是要追求效率的。。。只不過這種狀況跟工作無異了  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:15 | OwnWaterloo
@溪流
嗯, 這是一個繞開的方法, 然后通過別的機制去獲得F的返回類型與參數列表還有調用約定。

在我的那個case里, 返回類型、參數列表、調用約定都要獲取, 并對不同的調用約定做不同的事。
嗯, 就是thunk啦, 你懂的。

最終還是要獲取那些信息, 所以我直接用
template<R>
R call ( R (*f) )
來獲取了。 這機制叫啥名一下子想不起來了……


但這對g++依然不行。
產生的兩個函數相互依然是重定義。
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:18 | OwnWaterloo
@溪流
舉個例子嘛……

不行就再舉個, 比如登錄, 批量下載圖片……
嗯, 就是校內…… 我就是因為這個學python的。

最開始是打算用 libcurl, 但發現C/C++處理字符串太蛋疼……
又打算將 libcurl綁定到lua, 算練手(因為那時候python還不熟)
最終發現還是麻煩, 直接用python了……

嗯, wget 什么的我不熟……
用python做也算順便練習吧……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:22 | OwnWaterloo
就批量下載圖片這個case來說, C++能提高什么效率?
網絡傳輸才是大頭, 這C++是提升不了的。
也就提高解析html的效率…… 但那是多么蛋疼的事情……

5秒傳輸是死的。
0.5秒python解析, 與0秒(算極端情況)C++解析
體會不到差異……
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:23 | 溪流
@OwnWaterloo
呵呵

這種寫法

template <typename S>
class C;

template <typename R>
class C<R (__stdcall)()>
{

};

發現通不過。。。

還有這個:

template <typename T>
class C;

template <typename U>
class C<SomeTraits<U>::Result>
{

};

偏特化的尖括號里不能進行摸板元運算?


  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:24 | 溪流
@OwnWaterloo
嗯,從實際角度上來說確實這樣~~  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 01:28 | OwnWaterloo
@溪流
template <typename S>
class C;

template <typename R>
class C<R (__stdcall*)()> {};

template <typename R>
class C<R (__stdcall&)()> {};

"函數類型"是一個很灰色的地帶……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 10:50 | 飛舞的煙灰缸
@cexer
妙啊,想不到解決方案這么簡單。還為此郁悶了很久呢。只是看起來稍微有點冗余,我這樣有代碼潔癖的人的確不太容易想到。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 11:15 | 飛舞的煙灰缸
@溪流
前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅定了想法,解析之!。。讓使用者不需要查 MSDN。。。或許做不到這種程度吧。

這也是一個兩面的問題。用戶自己解析他至少可以去查MSDN,熟悉GUI的用戶可以很方便的上手;框架提供解析那要做到怎樣的程度呢,如果全部由框架解析那至少等大部分常用的消息都解析完成后這個框架才跑的起來,之后還要不斷的完善直到把所有非常用的消息也解析完畢,這是一個浩瀚的工程。
一個折中的辦法是提供常用消息的解析,同時用戶可以自己得到并處理WPARAM、LPARAM,就像WTL的用法。我自己反而覺得一堆使用WPARAM、LPARAM相同參數的處理函數看著整齊更賞心悅目。
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 11:18 | 飛舞的煙灰缸
GUI框架的復雜度主要是來源于實際要處理的問題的復雜度,這個是現實世界的復雜度沒辦法的事。框架做的少用起來復雜,框架做的多了學起來復雜用起來也未必簡單多少,個人觀點。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 12:32 | 溪流
@飛舞的煙灰缸
我覺得做或者不做都是一個方案,但是不同意折中。如果折中,框架沒法跟用戶解釋,為什么WM_LBUTTONUP做了額外的工作,WM_DESTROY就沒做呢?框架沒有理由認為哪些是常用的。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 16:16 | yrj
@OwnWaterloo
共同提高吧。

我覺得是需求的多樣性導致了實現的多樣性。雖然 C++ 語言不太適合動態消息綁定,但在有些情況下有這樣的需求,有這樣的需求就有人用 C++ 實現這種功能。

曾看到另外一篇相關的文章
function/bind的救贖
http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 18:47 | OwnWaterloo
@yrj
孟巖的是吧?
當時這篇文章在buzz上被分享是我就不予好評。
理由就是上面和cexer提到的, 完全的動態, 純面向對象而不是面向class是有代價的, 而且是C++必定不能承受的代價。
只知道說C++這不好, 那不好, 也不想想為什么。
他的文章(至少C++相關那些)在我看來都是水多料少。
也許時代不同了吧……  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 22:19 | yrj
受教了。

我并不執著于"是否需要在C++中實現"動態或者"C++必定不能承受的代價"。我想要知道的是每種方法的優缺點和它的適用條件,應用時根據不同的需求選擇適合的方法。在嵌入式GUI應用中強調運行效率及省電等情況下,會采用完全靜態的方法;如運行效率等條件可以放松的情況下,則采用一些動態的方法提升開發效率。
  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 22:28 | OwnWaterloo
@yrj
>> 我想要知道的是每種方法的優缺點和它的適用條件,應用時根據不同的需求選擇適合的方法。

嗯, 這是王道。
根據問題選合適的方案, 而不是將自己熟悉的方案去套各種問題。

關鍵是, 孟巖只提一方面(靈活性) 不提其代價。 片面的給C++抹黑。
仿佛能給C++抹黑就顯得自己多高水平似的。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-19 23:24 | 陳梓瀚(vczh)
@溪流
你可以去看看我的代碼哈,判斷兩個function是否==我做了。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-26 11:18 | guest
GCC可以通過,但VC2008報錯:Function0(const T &fun) 說fun是“引用的引用”  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上) 2011-01-26 12:02 | 溪流
@guest
08沒試過,就不知道了。不過后來有一個改進(在下篇最后說的),就是 Function<R()> 的第一個構造函數的參數去掉 const &,改成 T fun,同時 FunctionTraits 也不要了。這樣可能08下也能過了。  回復  更多評論
  
# re: C++ 下 Function 對象的實現(上)[未登錄] 2011-02-21 17:31 | C++愛好者
@OwnWaterloo
完全同意你的觀點,其實就讓大家按著一種方法寫那不是更好嗎?為啥還要搞那么多寫法,弄得大家都不統一,這不是畫蛇添足嗎?真不知道那些人是怎么想的。

我順便也發泄下我對C/C++有些不滿意的地方:
關于C++對象的隱式轉換,我覺得這個功能只有害處沒有好處(如果真要說有好處的話那就是可以少打點字)。比如說:
class Test
{
public:
Test(int a);
};

void func(const Test &t)
{
}

調用1:func(Test(1)); // 手動構造一個Test對象
調用2:func(1); // 自動進行隱式轉換

雖然調用2和調用1的操作方式可以說完全一樣,但調用2的可讀性不強,給人閱讀代碼帶來負擔,這又是何必呢?莫非真的是為了省那么幾個字母?

還有
void func()
{
}

void func(void)
{
}
這兩種寫法我更贊同前一種寫法。沒參數就沒參數嘛,為啥非得弄個void在括號里,看著多難看,當然返回值為void另當別論(我承認自己有點主觀,但是可以二選一,大家統一一下總可以吧)。  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成网站777色婷婷| 国产精品一区一区| 在线一区免费观看| 中日韩美女免费视频网站在线观看| 久久精品人人做人人爽| 久久高清免费观看| 久久爱91午夜羞羞| 久热精品视频| 亚洲国产欧美一区二区三区同亚洲 | 一区二区三区福利| 亚洲欧美一区二区原创| 欧美中文字幕不卡| 欧美乱人伦中文字幕在线| 国产精品国产亚洲精品看不卡15 | 日韩视频不卡| 午夜在线视频观看日韩17c| 欧美 日韩 国产 一区| 亚洲精品欧美专区| 久久美女艺术照精彩视频福利播放| 欧美大片免费久久精品三p| 国产精品一卡| 日韩一级网站| 亚洲国产精品一区制服丝袜| 亚洲特级片在线| 欧美成人免费一级人片100| 欧美日韩你懂的| 亚洲精品九九| 亚洲精品一区在线观看| 欧美肥婆在线| 黄色日韩网站视频| 美女视频一区免费观看| 亚洲欧美一区二区激情| 欧美视频一区| 欧美一区免费视频| 亚洲欧洲精品天堂一级 | 香蕉国产精品偷在线观看不卡| 久久人人爽国产| 久久久久成人精品免费播放动漫| 国产九九精品视频| 久久成人免费日本黄色| 久久激情综合网| 一本一本久久a久久精品综合麻豆| 亚洲韩国日本中文字幕| 欧美视频四区| 免费欧美日韩| 国产女优一区| 日韩视频免费| 亚洲国产日韩欧美在线99| 制服丝袜激情欧洲亚洲| 伊人久久婷婷色综合98网| 亚洲国产精品专区久久| 国产毛片久久| 在线一区二区日韩| 亚洲第一精品夜夜躁人人爽| 亚洲精品乱码久久久久| 国产一区视频在线看| 亚洲国产精品美女| 在线免费日韩片| 亚洲在线一区二区| 99xxxx成人网| 欧美a级理论片| 欧美在线观看视频一区二区三区| 欧美成人资源网| 免费看的黄色欧美网站| 国产日韩精品一区| 亚洲一区自拍| 久久av红桃一区二区小说| 国产精品夫妻自拍| 99国产精品国产精品久久| 麻豆精品在线播放| 久久亚洲影音av资源网| 国内精品久久久久久影视8| 午夜精品在线观看| 久久成人羞羞网站| 激情五月综合色婷婷一区二区| 亚洲欧美国产不卡| 久久一区二区三区超碰国产精品| 国产欧美日本| 免费一级欧美片在线播放| 欧美激情视频免费观看| 亚洲精品日本| 欧美日韩一区在线播放| 亚洲性线免费观看视频成熟| 欧美亚洲日本一区| 亚洲国产老妈| 国产精品亚洲欧美| 欧美大尺度在线观看| 亚洲一区二区三区在线看| 麻豆精品视频| 亚洲欧美日韩在线一区| 在线看国产日韩| 国产农村妇女精品| 欧美日韩精品免费观看视频完整| 欧美一区二区三区在线观看| 亚洲国产你懂的| 欧美freesex交免费视频| 亚洲精品国产精品乱码不99 | 国产一在线精品一区在线观看| 久久影院亚洲| 性色av一区二区三区红粉影视| 欧美成人高清视频| 久久久久久高潮国产精品视| 老司机精品视频网站| 亚洲精品久久久久中文字幕欢迎你| 激情小说亚洲一区| 亚洲第一久久影院| 亚洲精品日韩一| 国产欧美日韩另类一区| 91久久精品一区二区别| 亚洲第一成人在线| 亚洲欧美在线一区| 老司机精品视频一区二区三区| 亚洲精品在线视频观看| 一区二区三区精品国产| 亚洲欧美日韩中文播放| 久久精品国产欧美激情| 欧美成人午夜激情| 国产精品国产精品国产专区不蜜| 国产精品视频大全| 欧美日韩一区二区在线观看视频| 欧美日韩午夜精品| 国产欧美日韩精品在线| 亚洲精选久久| 久久久99爱| 一区二区三区四区蜜桃| 性色av一区二区三区在线观看 | 欧美黄色免费| 亚洲欧洲精品一区二区三区不卡| 亚洲第一伊人| 久久国产成人| 国产精品久久久久毛片软件| 国产欧美另类| 亚洲制服av| 欧美日韩伦理在线| 伊人色综合久久天天五月婷| 亚洲夜晚福利在线观看| 免费视频一区二区三区在线观看| 日韩一级不卡| 欧美成人日韩| 亚洲大胆视频| 欧美国产免费| 久久亚洲一区| 亚洲日本欧美日韩高观看| 久久久精品一区二区三区| 国产日韩欧美在线播放不卡| 亚洲狼人精品一区二区三区| 嫩模写真一区二区三区三州| 亚洲神马久久| 国产精品久久久久一区二区三区共 | 亚洲欧美日韩在线高清直播| 亚洲精品久久久久中文字幕欢迎你| 欧美中文在线观看| 国产亚洲精品综合一区91| 久久久久久久网站| 午夜在线成人av| 国产自产高清不卡| 久久亚洲视频| 欧美日韩国产电影| 亚洲伊人网站| 欧美在线视频全部完| 在线电影院国产精品| 亚洲国产网站| 国产精品你懂的在线欣赏| 久久精品免费| 老司机aⅴ在线精品导航| av成人老司机| 午夜在线一区| 亚洲精品小视频| 性刺激综合网| 99视频精品| 麻豆视频一区二区| 午夜一区二区三区不卡视频| 久久国产精品亚洲va麻豆| 亚洲免费成人av电影| 午夜精品视频在线| 99国产精品久久久久老师 | 麻豆成人在线| 国产精品久久久久三级| 久久精品一区四区| 欧美午夜一区二区福利视频| 欧美freesex交免费视频| 国产一区二区三区久久精品| 一区二区三区蜜桃网| 性欧美办公室18xxxxhd| 亚洲一区二区三区在线| 欧美黄色一级视频| 亚洲国产婷婷综合在线精品 | 99热这里只有成人精品国产| 久久精视频免费在线久久完整在线看| 亚洲午夜久久久| 国产精品mv在线观看| 新67194成人永久网站| 久久精品亚洲精品国产欧美kt∨| 国产日韩在线亚洲字幕中文| 亚洲一区二区在线播放| 久久久久五月天| 99国产精品久久| 欧美日韩免费观看一区| 一本久道久久久| 久久国内精品视频|