2011-03-17 46 views
1

這是一個奇怪的問題,因爲我已經知道'編碼'的答案。我只想更好地理解它爲什麼如此。有大師在這裏有一個解釋這些東西比C++標準更好的訣竅:)typename當定義地圖數據是一個函數指針與一個模板的浪花

下面我們有一種方法來定義一個抽象工廠模板,它基於字符串作爲關鍵字分配對象(這是一個人爲的例子): -

#include <iostream> 
#include <map> 
#include <string> 

using namespace std; 

template <typename T, typename TProduct> 
TProduct *MyFactoryConstructHelper(const T *t) 
{ 
    if (!t) return new T; 
    return new T(*static_cast<const T*>(t)); 
} 

template <typename TProduct> 
class AbstractFactory 
{ 
public: 
    typedef TProduct *(*MyFactoryConstructor)(const void *); 
    typedef map<string, MyFactoryConstructor> MyFactoryConstructorMap; 

    static TProduct *Create(const string &iName) 
    { 
    MyFactoryConstructor ctr = mTypes[iName]; 
    TProduct *result = NULL; 
    if(ctr) result = ctr(NULL); 
    return result; 
    } 

    template <typename T> 
    static bool Register(const string &iName) { 
    typedef TProduct*(*ConstructPtr)(const T*); 
    ConstructPtr cPtr = MyFactoryConstructHelper<T, TProduct>; 
    string name = iName; 
    mTypes.insert(pair<string,MyFactoryConstructor>(name, reinterpret_cast<MyFactoryConstructor>(cPtr))); 
    return(true); 
    } 

protected: 
    AbstractFactory() {} 
    static MyFactoryConstructorMap mTypes; 
}; 

template <typename TProduct> 
map<string, /*typename*/ AbstractFactory<TProduct>::MyFactoryConstructor> AbstractFactory<TProduct>::mTypes; 

這裏是我們如何使用它的一個例子: -

class MyProduct 
{ 
public: 
    virtual ~MyProduct() {} 

    virtual void Iam() = 0; 
}; 

class MyProductFactory : public AbstractFactory<MyProduct> 
{ 
public: 
}; 

class ProductA : public MyProduct 
{ 
public: 
    void Iam() { cout << "ProductA" << endl; } 
}; 

class ProductB : public MyProduct 
{ 
public: 
    void Iam() { cout << "ProductB" << endl; } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    MyProduct *prd; 
    MyProductFactory::Register<ProductA>("A"); 
    MyProductFactory::Register<ProductB>("B"); 

    prd = MyProductFactory::Create("A"); 
    prd->Iam(); 
    delete prd; 
    prd = MyProductFactory::Create("B"); 
    prd->Iam(); 
    delete prd; 

    return 0; 
} 

它不會編譯原樣,抱怨地圖沒有對數據的有效模板類型參數類型。但是,如果您在靜態成員定義中刪除了關於「typename」關鍵字的註釋,那麼所有內容都可以編譯並正常工作......爲什麼?

還有,我可以做得更好嗎? :)

+0

你應該至少指向你所指的那條線? – Nawaz 2011-03-17 05:11:10

+3

有一個很好的解釋,爲什麼'typename'是需要在堆棧溢出C + +常見問題條目,「[哪裏把」模板「和」typename「依賴名稱)(http://stackoverflow.com/questions/610245 /那裏到把最模板和類型名稱上依賴-名)」 – 2011-03-17 05:12:01

回答

2

該標準試圖允許實現解析,並且 在模板定義讀取 之前在任何實例化之前儘可能多地檢測模板中的錯誤。但是,C++不是 上下文無關,而且如果不知道 哪些符號是類型,哪些是模板,則很難(如果不是 )正確解析語句。如果符號 是依賴的(取決於模板參數) 您必須告訴編譯器何時它是類型或模板;否則,編譯器必須假定它是別的東西。 在這種情況下,您要告訴編譯器, AbstractFactory :: MyFactoryConstructor將類型命名爲 而不是別的。

如果,當模板實例,編譯器可以看到 到什麼符號是真正的約束,事實證明,你騙 (如AbstractFactory :: MyFactoryConstructor其實 一個int),那麼編譯器會生你的氣。

還要注意,AbstractFactory在需要typedef的定義之前定義爲 的事實不會改變 的任何內容。對於您正在實例化AbstractFactory的類型,可能總是會有一個明確的專門化處理 。

2

簡單的原因是,即使您和我在查看代碼時知道AbstractFactory :: MyFactoryConstructor是一種類型,編譯器不會 - 或者說確切地說,標準禁止瞭解這一點。就第一遍編譯過程而言,MyFactoryConstructor本身在模板內部尚未完全實現 - 可能是其他內容,比如靜態變量,它不允許作爲地圖的第二個模板參數,這需要一個類型。提供「typename」允許編譯器在第一次遇到它時立即將其視爲類型。