深度探索c++對象模型之template的錯誤報告

      考慮下面的template聲明【裏面有不少錯誤】:

template< class T >
class Mumble
{
public$: //第一處錯誤,非法標識符$
  Mumble( T t=1024 ) //第二處錯誤,t被初始化1024,但如果我們給mumble綁定的是char呢?
  :_t(t) //第三處錯誤,_t並不是哪一個mumble中的成員,tt纔是。這種錯誤一般會在類型檢查這個階段查找出來。每一個名稱必須綁定在一個定義身上,要不就會被判定錯誤!
  {
     if( tt != t ) //第四處錯誤, !=運算符有可能還沒有定義好【視T的真正綁定類型來定】,和第二點一樣,只有template的各個實體才能診斷出來。
       throw ex ex; //第五處錯誤,非法標識符ex ex;這種錯誤會在編譯時起的解析階段被發現,c++語言中一個合法的句子不允許一個標識符後面緊跟一個標識符。
    }
private:
  T tt;
};
在一個nontemplate class聲明中,這五個錯誤會在編譯時期就能查出來。但template class卻不同,舉個例子,所有與類型有關的檢驗,如果牽扯到template參數,都必須延遲到真正的具現【instantiation】操作時才發生,也就是說,第二處錯誤與第四處錯誤會在每個具現【instantiation】操作時才能被檢查出來,其結果會因爲不同的實際綁定類型而不同,比如:

Mumble<int> m; //合法的
則第二處與第四處就不是錯誤,但如果是

Mumble<int*> pm; //非法的
那麼第四處依然正確,但第二處肯定是錯誤!因爲在c++中,不允許將一個除了0之外的整數常量賦值給指針。

      那麼,什麼樣的錯誤會在編譯器處理template聲明時被找出來?這裏有一部分和template的處理策略有關。cfront對template的處理是完全解析【parse】,但不做類型檢驗,只有在每一個具現【instantiation】操作時才做類型檢驗,所以在這種parse策略之下,所有的語彙錯誤和解析錯誤都會在處理template聲明時被找出來。

      語彙分析器【lexical analyzer】會在第一處錯誤那裏捕捉到一個非法標識符,解析器【parser】會這樣標識它:(  public$: // caught ),表示這是一個不合法的卷標【label】,但解析器不會把“對一個未命名的member成員做出參考操作”視爲錯誤,所以第三處錯誤並不歸解析器負責,但解析器仍然會抓住第五處錯誤。

      在一個十分普遍的替代策略中,template聲明被收集爲一系列的“lexical tokens”,而parsing操作延遲到有真正具現【instantiation】操作時纔開始:每當看到一個instantiation,相關的token就會被推往parser,然後調用類型檢驗查找有無錯誤等等;面對先前出現的那個template聲明,“lexical tokenizing”會指出什麼錯誤嗎?事實上很少,只有第一處錯誤那裏的非法標識符會被找出,其餘的template聲明都會被解析爲合法的tokens並收集起來!

      Nonmember和member template function在具現【instantiation】發生之前也沒有做到完全的類型檢驗,這導致某些十分離譜的錯誤竟然可以編譯通過,例如下面這個:

template<class type>
class Foo
{
public:
  Foo();
  type val();
  void val(type v);
private:
  type _val;
}; //以上這些沒啥問題
...
template<class type>
double Foo<type>::helle(){ return this->world; } //錯誤在這裏!看裏面的hello和world,我們可有在Foo裏面聲明?
不論是cfront還是Sun編譯器亦或borland,都不會對上面的代碼產生怨言!


      再說一次,上面的策略,都是編譯器設計者自己的決定,template facility並沒有說不允許對template聲明的類型部分有更嚴格的檢查,當然,其實這樣的錯誤可以在編譯時起發現,只不過大家懶得這麼做罷了。





發佈了53 篇原創文章 · 獲贊 113 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章