智能指標實作 operator->*

Implementing operator->* for
Smart Pointers
(Dr. Dobb's Journal October 1999/10)

作者:Scott Meyers
譯者:陳崴

侯捷註:本文係北京《程序員》雜誌 2001/09 的文章。譯筆順暢,技術飽滿。
承譯者陳崴先生與《程序員》雜誌負責人蔣濤先生答允,
轉載於此,以饗臺灣讀者,非常感謝。

未得陳崴先生與蔣濤先生二人之同意,任何人請勿將此文再做轉載。


譯註:以下是本文所採用的幾個特別請讀者注意的術語:

client :客端。
type:型別。為避免和其他近似術語混淆,本文譯為「型別」而非「類型」。
instantiated:具現化。「針對一個 template,具體實現出一份實體」的意思。
instance:實體。「案例」是錯誤的譯法。
parameter:參數。或稱型式參數,形參。
argument:引數。或稱實質參數,實參。
user-defined:使用者自定

至於 class, object, data member, member function, constructor, destructor, template 等術語,皆保留不譯。


當我撰寫 More Effective C++: 35 Ways to Improve Your Programs and Designs (Addison-Wesley, 1995) 時,我在其中探討了一個主題:智能指標(smart pointers)。結果,我收到許多詢問,其中最有趣的一個問題來自 Andrei Alexandrescu,他問到:『一個真正很有智能的智能指標,該不該對 operator->* 實施多載化呢?我從未看過有人那麼做』。是啊,我也從未看過有人那麼做,所以我決定試試。我所獲得的結果深具啟發意義,遠比「實現 operator->*」更多,其中涉及極有趣也極有用的 templates 運用方式。

檢閱 operator->*

如果你和絕大多數程式員一樣,你大概不曾用過 operator->*。因此,在我解釋如何針對智能指標實作這個 operator 之前,我得先帶你檢閱一下它的內建行為。

假設有一個 class C,一個指標 pmf ,用以指向 C 的無參數 member function,以及一個指標 pc,用以指向 C object。以下運算式:

(pc->*pmf)();   // 在 *pc 身上喚起 member function *pmf。

會在「pc 所指的那個 object」身上喚起 pmf 所指的那個 member function。一如程式列表一所示,一個指向 member functions 的指標,其行為類似指向一般函式的指標,只是語法稍稍複雜些而已。透過這種方式來寫碼,環繞 pc->*pmf   的小括號是必要的,因為編譯器會把以下式子:

pc->*pmf();    // 錯誤!

視為:

pc->*(pmf());  // 錯誤!

 

程式列表一

class Wombat {          // wombats 是澳洲特產的一種有袋動物,
public:                 // 看起來有點像狗。

    int dig();          // 傳回 depth dug
    int sleep();        // 傳回 time slept
};

typedef int (Wombat::*PWMF)(); // PWMF:一個指向 Wombat member function 的指標。
Wombat *pw = new Wombat;  

PWMF pmf = &Wombat::dig;   // 令 pmf 指向 Wombat::dig
(pw->*pmf)();              // 這和 pw->dig(); 相同。
pmf = &Wombat::sleep;      // 令 pmf 指向 Wombat::sleep
(pw->*pmf)();              // 這和 pw->sleep(); 相同。

 

為支援 operator->* 而做的設計

就像許多運算子一樣,operator->* 也是二元的:它接受兩個引數。當我們針對智能指標實作 operator->*時,其左引數是個智能指標,指向一個型別為 T 的物件。其右引數是個指標,指向 class T 的一個 member function。呼叫 operator->*之後,我們對其回傳值唯一能做的事情就是給它一個參數列,使它成為一個「函式呼叫」。所以,operator- >* 的回傳型別必須是某種「operator()(所謂 function call operator)得以施行於上」的東西。operator->* 的回傳值代表一個審理中的、未完成的函式呼叫,所以我把 operator->* 傳回的物件型別稱為 PMFC,意思是一個 "Pending Member Function Call."

把這些整合在一起,於是我們獲得程式列表二的虛擬碼(pseudocode):

 

程式列表二(pseudocode)

class PMFC {               // "Pending Member Function Call"
public:
    ...
    return type operator()( parameters ) const;	// 譯註(1)
    ...
};

template<typename T>        // template,用於 T 智能指標,可以支援 operator->*。
class SP {                  
public:
    ...
    const PMFC operator->*( return type (T::*pmf)( parameters ) ) const;	// 譯註(2)
    ...
};
// 譯註:上述(1)的 return type 和 parameters 必須和 (2) 中的對應物完全一樣。

 

由於每一個 PMFC object 都代表著一個未完成的呼叫:呼叫 operator->* 所接收到的 member function,因此 member function 和 PMFC::operator() 所期待的參數列完全相同。為了簡化事情,我假設 T 的 member functions 不接受任何引數(稍後我會移除這項限制),這意味你可以將程式列表二重新定義為程式列表三

 

程式列表三

class PMFC {
public:
    ...
    return type operator()() const;
    ...
};

template<typename T>
class SP { 
public:
   ...
    const PMFC operator->*( return type (T::*pmf)() ) const;
    ...
};

 

上述「pmf  所指向之 member function」的回傳型別究竟會是什麼呢?可能是 int, double, 也可能是 const Wombat&。是的,它可能是任何型別。你應該將這種無窮可能的組合以 template 來表現。於是,operator->* 現在變成了一個 member function template。此外,PMFC 也變成了一個 template,因為不同的「operator->* 具現體」會傳回不同型別的 PMFC objects。每個 PMFC object 在其 operator()  被喚起時,都必須知道回傳型別是什麼。

一旦將它模板化(templatization)之後,你便可以捨棄虛擬碼(pseudocode),寫出真正的 PMFCSP::operator->*,如程式列表四

 

程式列表四

template<typename ReturnType>   // template,用於一個「回傳型別為 ReturnType」的
class PMFC {                    // 未完成的 member function call。
public:
    ...
    ReturnType operator()() const;
    ...
};

template<typename T>
class SP { 
public:
    ...
    template<typename ReturnType>
        const PMFC<ReturnType>
            operator->*( ReturnType (T::*pmf)() ) const;
    ...
};

 

無任何參數的 Member Functions

身為一個未完成的 member function call,PMFC 需要知道兩樣東西,才能實作其 operator():(1) 它究竟要呼叫哪個 member function,(2) 在哪個 object 身上喚起該 member function。PMFC constructor 應該是索取這些引數的一個合適地點。此外,將上述兩樣東西放在一個標準的 pair object 內似乎也是個好主意。於是我們獲得程式列表五

 

程式列表五

template<typename ObjectType,        // 提供 mem func 的那個 class。
         typename ReturnType,        // mem func 的回傳型別。
         typename MemFuncPtrType>    // mem func 的完整標記。
class PMFC { 
public:
    typedef std::pair<ObjectType*, MemFuncPtrType> CallInfo;

    PMFC(const CallInfo& info): callInfo(info) {}
    ReturnType operator()() const 
        { return (callInfo.first->*callInfo.second)(); } 
private:
    CallInfo callInfo;
};

 

乍見之下雖然似乎很複雜,其實相當簡單。當你產生一個 PMFC,你必須指出要呼叫哪一種 member function(譯註:這就是上述MemFuncPtrType的作用),並指出在哪一種 object 身上喚起它(譯註:這就是上述 ObjectType的作用)。稍後當你喚起 PMFCoperator() 函式,後者所喚起的便是這個「型別已被完整儲存(記錄)下來」的 member function。

請注意上述的 operator() 係根據語言內建的 operator->* 實作出來。要知道,只有當智能指標的「使用者自定之 operator->*」被呼叫,才會產生出一個 PMFC objects,這暗喻「使用者自定之 operator->*」應該根據「語言內建的 operator->*」實作出來。這和我們所熟知的以下事實是一個很好的對稱:「使用者自定之 operator->」的行為,和「語言內建之 operator->」彼此關連,因為 C++ 對於「使用者自定之 operator->」的每一個呼叫,最終都是(隱喻地)呼叫到「語言內建的 operator->」。如此的對稱現象令人安心,表示目前的設計走在正確的方向上。

你可能已經注意到了,ObjectType, ReturnType, 和 MemFuncPtrType   三個 template parameters 似乎有點贅餘。因為如果我們擁有 MemFuncPtrType,我們就能夠推演出 ObjectTypeReturnType。畢竟不論 ObjectTypeReturnType,都是 MemFuncPtrType 的一部份。的確,我們能夠使用「模板偏特化」(partial template specialization)技術,從 MemFuncPtrType 推導出 ObjectTypeReturnType,但由於支援「偏特化」技術之商用編譯器還不十分普及(譯註:例如 Visual C++ 6 就沒有支援),所以我沒有選擇這種作法。如果想基於「偏特化技術」來進行設計,相關細節請看稍後的「Partial Template Specialization 與 operator->*」方塊文字。

有了程式列表五中的 PMFC 實作內容,SP<T> operator->* 幾乎已經完成。它所傳回的 PMFC object 需要一個 object pointer 和一個 member function pointer 做為初值。一如程式列表六所示,智能指標習慣上會有一個 data member 用來存放 object pointer;至於 member function pointer,其實就是 operator->* 所接獲的引數。因此,程式列表七的代碼應該可以有效運作。以我所測試的兩個編譯器(Visual C++ 6 和 egcs 1.1.2)而言,的確如此。

 

程式列表六

template <typename T>
class SP {
public:
    SP(T *p): ptr(p) {}

    template <typename ReturnType>
        const PMFC<T, ReturnType, ReturnType (T::*)()>
            operator->*(ReturnType (T::*pmf)()) const 
                { return std::make_pair(ptr, pmf); }
    ... 
private:
    T* ptr;
};

程式列表七

#include <iostream>
#include <utility>
using namespace std;  

template<typename ObjectType, typename ReturnType, typename MemFuncPtrType>
class PMFC { ... };              // 一如以往

template <typename T>            // 一如以往
class SP { ... }; 

class Wombat { 
public: 
    int dig() 
    {
        cout << "Digging..." << endl;
        return 1;
    } 
    int sleep() 
    {
        cout << "Sleeping..." << endl;
        return 5;
    }
};

int main()
{                                     // 一如以往,PWMF 是個指標, 
    typedef int (Wombat::*PWMF)();    // 指向一個 Wombat member function

    SP<Wombat> pw = new Wombat;

    PWMF pmf = &Wombat::dig;   // 令 pmf 指向 Wombat::dig 
    (pw->*pmf)();              // 喚起我們的 operator->*;
                               // 印出 "Digging..." 

    pmf = &Wombat::sleep;      // 令 pmf 指向 Wombat::sleep 
    (pw->*pmf)();              // 喚起我們的 operator->*;
}                              // 印出 "Sleeping..."

 

是的,我發現了,這份程式碼會出現資源漏洞(resource leak),因為被 new 出來的 Wombat  從未被 delete。此外,這份程式碼使用了一個 using directive(也就是 using namespace std;),而其實使用 using declarations 也就夠了。請試著把焦點放在 SP::operator->* PMFC 的互動上,不要太拘泥如此小節。如果你了解 (pw-> *pmf)() 的行為由來,毫無問題你的實力必可輕易修正上例關於編程風格上的缺失。

順帶一提,由於 operator->* member functions 和所有的 PMFC member functions 都是 inline 函式(雖然並無明顯宣告),因此,你可以預期,針對以下述句所產生出來的碼:

(pw->*pmf)();

其中運用 SP  和 PMFC 的情況,將和以下等值述句所產生出來的碼沒有兩樣:

(pw.ptr->*pmf)();

其中只使用內建運算動作。因此,使用 SP's 的多載化 operator->* 和 PMFC's 的多載化 operator(),執行期成本可以為零:程式碼的額外成本為 零,資料的額外成本也為零。當然,實際成本視你的編譯器的最佳化能力、你的標準程式庫對於 pairmake_ pair 的實作而定。以我所測試的兩個編譯器(及其附帶的標準程式庫)而言,打開所有最佳化選項後,有一個編譯器會獲得執行期零成本的 operator->* 實作碼,另一個不會。

加上對 const Member Functions 的支援

請仔細看看 SP<T>operator->* 函式的正式參數:ReturnType (T::*pmf)()。讓我更明確地提示你,它不是 ReturnType (T::*pmf)() const。這意味,沒有任何一個「指向 const member function」的指標可以被傳遞給 operator->*,這也就意味 operator->* 不支援 const member functions。如此露骨的不公平待遇在一個具有良好設計的軟體系統中是毫無地位的。幸運的是,我們很容易破除這項限制。只要為 SP 加上第二個 operator->* template,整份設計就可適用於「指向 const member functions」的指標;見程式列表八。有趣的是,PMFC 的內容不需有任何改變,因為其型別參數 MemFuncPtrType 會繫結到任何一個 member function pointer 型別身上,不論那個 member function 是否為 const

 

程式列表八

template <typename T>
class SP {
public:
    ...                     // 如前

    template <typename ReturnType>
        const PMFC<T, ReturnType, ReturnType (T::*)() const>  // 加上 const
            operator->*(ReturnType (T::*pmf)() const) const   // 加上 const
                { return std::make_pair(ptr, pmf); }

    ...                     // 如前
};

支援「擁有參數的 Member Functions」

有了零參數的經驗在手,我們接下來設法支援「一個指標,指向 member function,該 member function 擁有一個參數」。整個過程出乎意料地簡單,因為你唯一需要做的就是修改 operator->* 所接受的參數型別,然後透過 PMFC 傳播這項改變。事實上,你唯一需要做的就是為operator->* 增加一個新的 template 參數,其型別應該是被指向的 member function 所能(所願)接受的參數型別。然後修改其他每一樣需要與此保持一致性的東西。此外,由於 SP<T> 也應該同時支援「接受零個參數」的 member functions 和「接受一個參數」的member functions,所以你應該為它添加一個新的 operator->* template。在程式列表九中,我只展示對於 nonconst member functions 的支援,事實上對 const member functions 的支援也應該同時完成。

 

程式列表九

template <typename ObjectType, typename ReturnType, typename MemFuncPtrType>
class PMFC {
public:
    typedef pair<ObjectType*, MemFuncPtrType> CallInfo;

    PMFC(const CallInfo& info)
   : callInfo(info) {}

    // 支援 0 個參數
    ReturnType operator()() const 
        { return (callInfo.first->*callInfo.second)(); }
    // 支援 1 個參數
    template<typename Param1Type>
    ReturnType operator()(Param1Type p1) const
            { return (callInfo.first->*callInfo.second)(p1); }
private:
    CallInfo callInfo;
}; 

template <typename T>
class SP {
public:
    SP(T *p): ptr(p) {} 

    // 支援 0 個參數
    template <typename ReturnType>
        const PMFC<T, ReturnType, ReturnType (T::*)()> 
            operator->*(ReturnType (T::*pmf)()) const
                { return std::make_pair(ptr, pmf); }

    // 支援 1 個參數
    template <typename ReturnType, typename Param1Type>
        const PMFC<T, ReturnType, ReturnType (T::*)(Param1Type)>
            operator->*(ReturnType (T::*pmf)(Param1Type)) const
                { return std::make_pair(ptr, pmf); }
    ... 
private:
    T* ptr;
};

一旦你能夠處理零個參數和一個參數,就很容易處理任意個數的參數。讓我做個整理,為了支援「具有 n 個參數」的  member functions,請在 SP  中宣告兩個 member template operator->*,一個用來支援 nonconst member functions,一個用來支援 const 版本。每一個 operator->* template 都應該獲得 n+1 個型別參數,其中 n 個做為函式參數使用,剩餘那個用來指定函式回傳型別。接下來,在 PMFC 中加上對應的 operator() template,就大功告成了。

 

將所有對 operator->* 的支援工作包裝起來

許多應用軟體對智能指標有多種不同的變化運用,如果他們必須針對每一種應用,重複上述工作,他們一定不會高興。如果你想看看智能指標的變化應用,包括像殺手一般酷的 C++ 程式,請看 Kevin S. Van Horn 的網站 http:// www.xmission.com/ ~ksvsoft/code/smart_ ptrs.html。幸運的是,對 operator->* 的所有支援工作,可以被包裝為一個 base class 型式,如程式列表十。

程式列表十

template <typename T>   // 這是一個 base class,用來包裝智能指標
class SmartPtrBase {    // 對 operator->* 的支援。
public:
    SmartPtrBase(T *initVal): ptr(initVal) {}

    // 支援 0 個參數。
    template <typename ReturnType>
        const PMFC<T, ReturnType, ReturnType (T::*)()> 
            operator->*(ReturnType (T::*pmf)()) const
                { return std::make_pair(ptr, pmf); }

    // 支援 1 個參數。
    template <   typename ReturnType, typename Param1Type>
        const PMFC<T, ReturnType, ReturnType (T::*)(Param1Type)>
            operator->*(ReturnType (T::*pmf)(Param1Type)) const
                { return make_pair(ptr, pmf); }
    ...
protected:
    T* ptr;
};

凡是希望提供 operator->*支援能力的智能指標,只需繼承 SmartPtrBase 即可。但是,只有當「智能指標內含一個一般指標,用以完成真正的指向動作」時,上述設計才適用。「內含一般指標」是最常見的一種智能指標設計方式,但還有其他不同的設計,這類不同的設計可能需要以不同於此處所描述的方式,將 operator->* 機能包裝起來。其中最好的方式或許是利用 private 繼承機制,因為如果使用 public 繼承機制,便有必要為 SmartPtrBase 加上一個 virtual destructor,因而增加其大小(以及其所有 derived classes 的大小)。Private 繼承機制可以避免付出這樣的代價,雖然,它需要一個 using declaration(見程式列表十一)以便讓「以 private 方式繼承而來的 operator->* templates」成為 public。如果要把它們包裝得更好,可以將 SmartPtrBasePMFC template 都放進一個命名空間(namespace)中。

 

程式列表十一

template <typename T>
class SP: private SmartPtrBase<T> {
public:
    SP(T *p ): SmartPtrBase<T>(p) {}

    using SmartPtrBase<T>::operator->*;    // 讓「private 繼承而來的
                                           // operator->* templates」成為 public。

    // 智能指標的一般函式放在這兒。operator->* 機能則是藉由繼承機制獲得。
};

鬆散的結尾

在我完成針對智能指標而做的 operator->* 支援工作後,我把我的解法公佈於 Usenet 的消息群組 comp.lang.c++.moderated,看看我是否遺漏了什麼。很快地 Esa Pulkkinen 做出了以下言論:

你的方法至少有兩個問題:

1. 你無法使用  pointers to data members(雖然這很容易解決)。

2. 你無法使用「使用者自定(user-defined)」的 pointers-to-members。如果有人多載化了 operator->*,令它接受「行為類似 member pointers」的某些 objects,你可能會想要在你的 smart pointer class 中支援如此的 "smart pointers to members" 。不幸的是,你需要 traits classes 才能獲得這種多載化後的operator->* 的回傳型別。

Smart pointers to members! 喔歐,Esa 是對的。(事實上比我最初所了解的還要正確。就在撰寫本文草稿之後的很短時間內,我的一個客戶對我展現了一個問題,該問題可以使用 smart pointers to members 自然地解決掉。我也很驚訝。)幸運的是,本文已經夠長了,我可以在這裡停下來,把解決 Esa 的評論的工作留給讀者。我決定這麼做。

摘要

如果你的目標是要讓你的智能指標(smart pointers)的行為儘可能相容於內建(原生)指標,你應該支援 operator->*,就像內建指標的作為一樣。運用 class templates 和 member templates,我們得以比較輕鬆地實作出如此的支援能力。把這些實作碼包裝為一個 base class,可以增加其復用性,方便其他智能指標的設計者。

致謝

Andrei Alexandrescu 除了引起我對 operator->* 的最初興趣,也幫我簡化了我對 PMFC 的實作。Andrei 還針對本文早期草稿提供了極富深刻意義的見解,並附帶原始碼。Esa Pulkkinen 和 Mark Rodgers 也做了相同的貢獻。他們對本文提供的協助,使我受惠良多。

作者

Scott 是一位 C++ 顧問,也是 Effective C++ CD-ROM, Effective C++, 和 More Effective C++ 的作者。你可以透過 http://www.aristeia.com/ 和他聯絡。

譯者

陳崴,自由撰稿人,專長 C++/Java/OOP/GP/DP。

 

 


sidebar : Partial Template Specialization 與 operator->*


在我撰寫本文期間,Esa Pulkkinen 和 Mark Rodgers 告訴我,模板偏特化技術(partial template specialization)可用來從一個「 pointer to member function」型別中萃取出該 member function 所屬的 class 型別,以及函式回傳型別。唯一需要的是施行 traits 技術,那是一種被廣泛運用於 C++ 標準程式庫的技術。關於 traits 技術,請參考 Nathan Myers 的文章:"Traits: A New and Useful Template Technique," C++ Report, June 1995,此文也出現於 http://www.cantrip .org/traits.html。

Mark Rodgers 建議程式列表十二所列的那些 templates,它們用來接受零個參數或一個參數的 member functions。要將它們擴充為接受更多參數,當然毫無問題而且作法十分直接了當。有了這些 templates,PMFC 就可以簡化為「只需一個型別參數,亦即 MemFuncPtrType」。那是因為,另兩個型別參數ObjectTypeReturnTypecan 可根據 MemFuncPtrType 推導出來:

程式列表十二

template <typename T>                       // traits class
struct MemFuncTraits {};

template <typename R, typename O>           // partial specialization
struct MemFuncTraits<R (O::*)()> {          // for zero-parameter
    typedef R ReturnType;                   // non-const member
    typedef O ObjectType;                   // functions
};

template <typename R, typename O>           // partial specialization
struct MemFuncTraits<R (O::*)() const> {    // for zero-parameter
    typedef R ReturnType;                   // const member
    typedef O ObjectType;                   // functions
};

template <typename R, typename O, typename P1>  // partial specialization
struct MemFuncTraits<R (O::*)(P1)> {            // for one-parameter
    typedef R ReturnType;                       // non-const member
    typedef O ObjectType;                       // functions
};

template <typename R, typename O, typename P1> // partial specialization
struct MemFuncTraits<R (O::*)(P1) const> {     // for one-parameter
    typedef R ReturnType;                      // const member
    typedef O ObjectType;                      // functions
};

運用這些 templates,我們便可獲得程式列表十三中新的PMFC。我不打算在這裡告訴你 traits 的技術細節,也不打算解釋為什麼 typename 必須出現在 template 內的型別名稱之前。上述這些 templates 可以大量降低 smart pointer classes「欲支援 operator->*」時的工作量。事實上,Mark Rodgers 提醒了我們,單單一個 operator->* template 就可以支援所有可能的 member function pointers,不論後者所指向的 member functions 接受多少個參數,也不論那些 member functions 是否為 const。是的,只要將 SP(或 SmartPtrBase)中所有的 operator->* templates 全部取代程式列表十四的碼即可。型別參數 MemFuncPtrType 將被繫結於任何一種 pointer to member function 型別身上,不論該 member function  的參數個數、回傳型別、常數性(constness)。整個 pointer to member function 型別會被傳遞給 PMFC,在那裡,偏特化機制(partial specialization)會起而行,將我們所在乎的各個型別抓出來。

程式列表十三

template <typename MemFuncPtrType>
class PMFC {
public:
    typedef typename MemFuncTraits<MemFuncPtrType>::ObjectType ObjectType;
    typedef typename MemFuncTraits<MemFuncPtrType>::ReturnType ReturnType;

    ...                 // 一如以往
};

程式列表十四

template <typename MemFuncPtrType>
const PMFC<MemFuncPtrType>
operator->*(MemFuncPtrType pmf) const
{ return std::make_pair(ptr, pmf); }

 

 


陳崴註1:完整程式碼如下,並附個人測試心得:

// vc6[x]  cb4[x]  gcc2.91.57_on_win32[o]
#include <iostream>
#include <utility>   // for pair and make_pair()
using namespace std;
// partial specialization 技術只有 GCC 和 BCB 支援。VC6 失敗。
template <typename T>
struct MemFuncTraits { };
template <typename R, typename O>
struct MemFuncTraits<R (O::*)()> {
  typedef R ReturnType;
  typedef O ObjectType;
};
template <typename R, typename O>
struct MemFuncTraits<R (O::*)() const> {
  typedef R ReturnType;
  typedef O ObjectType;
};
template <typename R, typename O, typename P1>
struct MemFuncTraits<R (O::*)(P1)> {
  typedef R ReturnType;
  typedef O ObjectType;
};
template <typename R, typename O, typename P1>
struct MemFuncTraits<R (O::*)(P1) const> {
  typedef R ReturnType;
  typedef O ObjectType;
};
template <typename MemFuncPtrType>
class PMFC {
public:
  typedef typename MemFuncTraits<MemFuncPtrType>::ObjectType ObjectType;
  typedef typename MemFuncTraits<MemFuncPtrType>::ReturnType ReturnType;
  typedef std::pair<ObjectType*, MemFuncPtrType> CallInfo;
  PMFC(const CallInfo& info) : _callinfo(info) { }
  // support for 0 parameter
  ReturnType operator()() const
    { return (_callinfo.first->*_callinfo.second)(); }
  // support for 1 parameter
  template <typename Param1Type>
    ReturnType operator()(Param1Type p1) const
      { return (_callinfo.first->*_callinfo.second)(p1); }
private:
  CallInfo _callinfo;
};
template <typename T>
class SmartPtrBase {
public:
  SmartPtrBase(T *p) : ptr(p) { }
  template <typename MemFuncPtrType>
    const PMFC<MemFuncPtrType> operator->*(MemFuncPtrType pmf) const
      {  return std::make_pair(ptr, pmf); }
private:
  T* ptr;  // dumb pointer
};
template <typename T>
class SP : public SmartPtrBase<T> {   // (1) 原文採用 private 繼承,GCC 失敗。註
public:
  SP(T *p) : SmartPtrBase<T>(p) { }
  using SmartPtrBase<T>::operator->*;  // (2) 在 G++ 中可編譯但無作用
  // normal smart pointer functions would go here.
};
/*
註:
原文 (1) 採用 private 繼承,但是 GCC 不支援「以 using declaration 突破 
private inheritance的限制」(可卻又允許 (2) 通過編譯,怪!),造成 main() 
之中凡使用 pw->* 即出現編譯錯誤。
只好在此改為 public。既然採用 public inheritance,(2) 的有無也就無關宏旨了。
但是,《C++ Primer 中文版》p.981 講「以 using declaration 突破 
private inheritance 的限制」,
我以 981.cpp 試之,G++ 卻又表現良好。為什麼?
請參考:well-known bug in G++: "Using declarations in classes do not work!"
見 Frequently Reported Bugs in GCC 2.95 (http://gcc.gnu.org/bugs.html)
*/
class Wombat {
public:
  int dig() { cout << "Digging..." << endl;  return 1; }
  int sleep() { cout << "Sleeping..." << endl;  return 5; }
  int eat() const { cout << "Eatting..." << endl;  return 7; }
  int move(int op) { cout << "Moving..." << endl;  return 9; }
};
typedef int(Wombat::*PWMF)();
typedef int(Wombat::*PWMFC)() const;
typedef int(Wombat::*PWMF1)(int);
void main()
{
  SP<Wombat> pw = new Wombat;
  PWMF pmf = &Wombat::dig;
  (pw->*pmf)();               // Digging...
  pmf = &Wombat::sleep;
  (pw->*pmf)();               // Sleeping...
  PWMFC pmfc = &Wombat::eat;
  (pw->*pmfc)();              // Eatting...
  PWMF1 pmf1 = &Wombat::move;
  (pw->*pmf1)(2);             // Eatting...
}

陳崴註2:作者在文中提到 wombat 是一種澳洲特產的一種有袋動物,看起來有點像狗。但從像片來看並非如此。wombat是夜行性動物,喜歡掘土,並不凶猛。下面是 wombat 的玉容:

wombat.jpg (32093 bytes)

作者也許想成另一種澳洲特產dingo,那是野生而極似狗的凶猛動作,有攻擊性,曾有小孩被這種動物咬死。

dingo.jpg (12311 bytes)

(感謝華邦公司 CTWu 提供像片與資料)