簡體   English   中英

在哪個位置發生模板實例化綁定?

[英]At which point occurs template Instantiation binding?

此代碼來自Bjarne Stroustrup的“C ++編程語言”(C.13.8.3實例化綁定點)

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

void h()
{
    extern g(double);
    f(2);
}

他提到:

這里,f()的實例化點恰好在h()之前,因此在f()中調用的g()是全局g(int)而不是局部g(double)。 “實例化點”的定義意味着模板參數永遠不能綁定到本地名稱或類成員。

void h()
{
    struct X {}; // local structure
    std::vector<X> v; // error: can't use local structure as template parameter
}

我的問題是:

  1. 為什么第一個代碼有效? g()稍后聲明,我真的得到G ++ 4.9.2的錯誤,那時g沒有被聲明。

  2. extern g(double) - 這是如何工作的? 因為在函數重載的情況下返回值無關緊要,那么我們可以在前向聲明中錯過它嗎?

  3. f()的實例化點就在h()之前 - 為什么? 在調用f(2)時它會被實例化是不合邏輯的? 就在我們稱之為的地方, g(double)已經在范圍內了。

  4. “實例化點”的定義意味着模板參數永遠不能綁定到本地名稱或類成員 - 在C ++ 14中是否已更改? 我在使用C ++(G ++ 4.9.2)時遇到錯誤,但是在C ++ 14(G ++ 4.9.2)中沒有出錯。

“1985年,第一版的C ++編程語言發布,成為該語言的權威參考,因為還沒有官方標准 。” wiki C ++歷史因此它在C ++ 11和C ++ 14之間沒有變化。 我可以假設(並且請帶着一點點鹽)它在“預標准化”和標准化之間發生了變化。 也許更了解C ++歷史的人可以在這里獲得更多的亮點。

至於實際發生的事情:


首先讓我們擺脫簡單的方式:

extern g(double);

這是無效的C ++。 從歷史上看,不幸的是C允許遺漏類型。 在C ++中,你必須編寫extern void g(double)


接下來,讓我們忽略g(double)重載來回答你的第一個問題:

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

int main()
{
    f(2);
}

在C ++中有臭名昭着的兩階段名稱查找:

  • 在第一階段,在模板定義中,解析所有非依賴名稱 不這樣做是一個很難的錯誤;
  • 在模板實例化時,在第二階段解析從屬名稱。

規則有點復雜,但這就是它的要點。

g取決於模板參數T因此它通過第一階段。 這意味着如果你從不實例化f ,代碼編譯就好了。 在第二階段, fT = int實例化。 現在搜索g(int) ,但未找到:

 17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup g(value); ^ 24 : note: in instantiation of function template specialization 'f<int>' requested here f(2); ^ 20 : note: 'g' should be declared prior to the call site void g(int v); 

為了使任意名稱g能夠通過飛行顏色,我們有幾個選擇:

  1. 先前聲明g
void g(int);

template <class T>
void f(T value)
{
    g(value);
}
  1. 帶來gT
template <class T>
void f(T)
{
    T::g();
}

struct X {
   static void g();
};

int main()
{
    X x;
    f(x);
}
  1. 帶來gT經由ADL:
template <class T>
void f(T value)
{
    g(value);
}

struct X {};

void g(X);

int main()
{
    X x;
    f(x);
}

這些當然會改變程序的語義。 它們旨在說明您在模板中可以和不可以擁有的內容。


至於為什么ADL找不到g(int) ,但找到g(X)

§3.4.2依賴於參數的名稱查找[basic.lookup.argdep]

  1. 對於函數調用中的每個參數類型T,有一組零個或多個關聯的命名空間以及一組零個或多個關聯的類要考慮[...]:

    • 如果T是基本類型,則其關聯的命名空間和類集都是空的。

    • 如果T是類類型(包括聯合),則其關聯的類是:類本身; 它所屬的成員,如果有的話; 及其直接和間接基類。 其關聯的名稱空間是其關聯類是成員的名稱空間。 [...]


最后我們得出為什么extern void g(double); 找不到內部主要內容:首先,我們發現如果f定義之前聲明了g(fundamental_type)則會找到它。 所以讓我們在main里面使它成為void g(X) ADL找到了嗎?

template <class T>
void f(T value)
{
    g(value);
}

struct X{};


int main()
{
  X x;
  void g(X);

  f(x);
}

不會。因為它與X (即全局命名空間)不在同一名稱空間中,所以ADL找不到它。

證明g不在全球范圍內

int main()
{
  void g(X);

  X x;
  g(x); // OK
  ::g(x); // ERROR
}

34:錯誤:全局命名空間中沒有名為'g'的成員; 你的意思是'g'嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM