深度探索c++對象模型之template中的名稱決議方式

      我們應該能夠區分以下兩種意義:一個是c++ standard標準中的“scope of the template definition”【模板定義域】,另一個是c++ standard標準中的“scope of the template instantiation”【模板具現域】。第一種情況舉例說明:
// scope of the template definition【模板定義域】

extern double foo(double);

template<class type>
class ScopeRules
{
public:
  void invariant() {  _member = foo( _val ); }
type type_dependent() { return foo( _member ); }

private:
  int _val;
  type _member;
}

      然後第二種情況也舉例說明:

// scope of the template instantiation【模板具現域】

extern int foo(int);

ScopeRules<int> sr;


      在ScopeRules template中有兩個foo版本:在“scope of the template definition”中,只有一個foo函數聲明位於scope之內。然而在“scope of the instantiation”中,兩個foo函數版本都位於scope之內。這時如果我們有一個這樣的調用操作:

// scope of the template instantiation

sr.invariant();

那麼在invariant中的【_member = foo( _val );】,究竟會調用哪一個函數實體呢?是scope of the template declaration【模板聲明域】中的“extern double foo( double )”,還是scope of the template instantiation【模板具現域】中的“extern int foo( int )”?如果您因爲在我們例子中用int具現的ScopeRules而選擇了後者,那麼很可惜您選錯了,因爲正確答案是意料之外的double foo。。。

      至於原因,讓我們來看一下書中的解釋:在template之中,對於一個非成員名字【如本例中invariant裏面foo】的決議結果,是根據這個名字的使用是否與“用來具現該模板的實際參數類型”有關與否來決定的,如果非成員名字的使用和具現模板用的實際參數沒有關聯,那麼就以“scope of the template declaration【模板聲明域】”來決定名字歸屬;然而如果它們有關聯,那麼就以“scope of the template instantiation【模板具現域】”來決定名字歸屬;回頭看我們上面的例子可以發現,invariant中的foo與我們用來具現模板的參數int沒有關聯,所以使用“scope of the template declaration”中的double版本foo函數,下面是更詳細的解釋:

// the  resolution fo foo() is not dependent on the template argument【foo函數的決議結果並不依賴於模板參數】
_member = foo( _val );
_val在我們的模板聲明中,已經被定義成一個int類型,也就是說,_val是一個類型不會變的模板類成員,無論具現這個ScopeRules的實際參數是什麼類型,都不會影響到_val本身。還有,函數的決議結果只和函數的signature【原型】有關,和函數的返回值類型什麼的無關,所以,模板具現後的_member類型並不會影響到哪一個foo函數體被選中,總之就是foo的調用與具現ScopeRules的參數毫無關聯!所以調用操作必須根據“scope of the template declaration”來決議,而在“scope of the template declaration”域中,只有一個double版本foo函數,所以自然只有一個候選者。【此外要注意的是,這種行爲不能以一個簡單的宏擴展——比如使用一個#define宏——重現之】。

      下面讓我們來看看“與具現模板類型”【type-dependent】有關的用法:

sr.type_dependent();
這個函數的內容如下:

return foo(_member);
這個例子與上一個例子不同,因爲_member肯定與具現ScopeRules的參數有關:該參數將決定_member的實際類型。所以這一次foo必須在“scope of the template instantiation”域中被決議,到了這個域中就有了兩個foo版本函數,但由於_member的被具現後的類型是int,因此這次就是int版本的foo函數出線。但是,如果ScopeRules是被unsigned int或long具現出來,那麼這裏的foo選擇就會模糊不清。最後,如果ScopeRules是被某一個用戶自己的自定義類類型具現出來,而該類沒有針對int和double實現conversion運算符【轉換運算符】,那麼foo的調用操作會被標識錯誤!但不管情況如何,都是以“scope of the template instantiation”來決定,而不是“scope of the template declaration”來決定。

      這意味着編譯器必須保持兩個scope contexts【上下文範圍】:

1):“scope of the template declaration”,用來專注於一般的template class;

2):“scope of the template instantiation”,用來專注於特定的實體;

編譯器的resolution【決議】算法必須決定哪一個纔是適當的scope,然後在其中搜索適當的name。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章