effective C++ 條款 42:瞭解typename的雙重意義

template聲明式中,class和typename這兩個關鍵字意義徹底相同html

template<class T> class Widget;c++

template<typename T> class Widget;spa

有時候你必定要用typename,指針

能夠在template中指涉的兩種名稱:htm

template <typename C> 
void print2nd(const C& container) 

    if (container.size() >= 2) 
    { 
        C::const_iterator iter(container.begin()); 
        ++iter; 
        int value = *iter; 
        std::cout << value; 
    } 
}對象

iter的類型是C::const_iterator 其實是什麼必須取決於template參數C。template內出現的名稱若是相依於某個template參數,稱之爲從屬名稱(dependent names)。若是從屬名稱在class內呈嵌套狀,稱之爲嵌套從屬名稱(nested dependent name)。C::const_iterator 就是這樣一個名稱嵌套從屬名稱。blog

value類型int。不依賴任何template參數的名稱。稱爲非從屬名稱(non-dependent name)。ci

嵌套從屬名稱可能致使解析的困難:get

template <typename C> 
void print2nd(const C& container) 

    C::const_iterator* x
}編譯器

看起來咱們好像聲明一個local變量,是個指針,指向一個C::const_iterator。 但它之因此被那麼認爲,是由於咱們「已經知道」C::const_iterator 是個類型。若是C::const_iterator 不是個類型呢?若是C有個static成員變量碰巧被命名爲const_iterator。過期x碰巧是個global變量名稱,那樣上述代碼就是一個相乘動做,C::const_iterator 乘以x。撰寫c++解析器的人必須操心全部可能的輸入。

在咱們知道C之前,沒有任何辦法能夠知道C::const_iterator 是否爲一個類型。而當編譯器開始解析template print2nd時,還沒有肯定C是什麼東西。

c++有個規則能夠解析此一歧義狀態:若是解析器在template中遭遇一個嵌套從屬名稱,它便假設這個名稱不是個類型,除非你告訴它是。缺省狀況下從屬名稱不是類型。此外還有個例外。

因此上述代碼不是有效的c++代碼。咱們必須告訴c++說C::const_iterator 是個類型。只要緊鄰它以前放置關鍵字typename便可:

template <typename C>//這個合法的c++代碼 
void print2nd(const C& container) 

    if (container.size() >= 2) 
    { 
        typename C::const_iterator iter(container.begin()); 
        ++iter; 
        int value = *iter; 
        std::cout << value; 
    } 
}

typename只用來驗明嵌套從屬類型名稱;其餘名稱不應有它存在。

template <typename C> 
void f(const C& container, //不容許使用typename 
       typename C::iterator iter);//必定要使用typename

typename必須做爲嵌套從屬類型名稱的前綴詞這一規則的例外是,typename不能夠出如今base classes list內的嵌套從屬類型名稱以前,也不可在member initialization list(成員初始化列表)中做爲base class修飾符。例如:

template <typename T> 
class Derived: public Base<T>::Nested{//base class list中不容許「typename」 
public: 
    explicit Derived(int x) 
        :Base<T>::Nested(x)//mem.init.list中不容許「typename」 
    { 
       typename Base<T>::Nested temp;//嵌套從屬類型既不在base class list中也不在mem.init.list中, 
    }                                                         //做爲一個base class修飾符需加上typename 
};

讓咱們看一個typename例子:一個function template,他接受一個迭代器,而咱們打算爲該迭代器指涉的對象作一份復件temp:

template <typename IterT> 
void workWithIterator(IterT) 

    typename std::iterator_traits<IterT>::value_type temp(*iter); 
}

這是個標準trait class的一種運用(條款47),至關於說「類型IterT之對象所指之物的類型」。若是IterT是vector<int>::iterator,temp的類型就是int,若是IterT是list<string>::iterator,temp的類型就是string。因爲std::iterator_traits<IterT>::value_type是個嵌套從屬類型名稱(value_type被嵌套於iterator_traits<IterT>以內而IterT是個template參數),因此必須在它以前放置typename。

這麼長你確定會想創建一個typedef。對於traits成員名稱如value_type,廣泛習慣是設定typedef名稱用以表明某個traits成員名稱:

template <typename IterT> 
void workWithIterator(IterT) 

    typedef typename std::iterator_traits<IterT>::value_type value_type; 
    value_type temp(*iter); 
}

分類:  c/c++
相關文章
相關標籤/搜索