2016-10-10 60 views
20

此代碼是從 「C++程序設計語言」 由Bjarne Stroustrup的(實例化的C.13.8.3處裝訂)在哪一點發生模板實例化綁定?

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

void g(int v); 

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

他提到:

Here, the point of instantiation for f() is just before h(), so the g() called in f() is the global g(int) rather than the local g(double). The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member.

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)而出錯。

+2

「在1985年,C++編程語言的第一個版本發佈,這成爲該語言的明確的參考,因爲**有沒有但官方標準**「。 [wiki](https://en.wikipedia.org/wiki/C%2B%2B#History)所以它在'C++ 11'和'C++ 14'之間沒有變化。它在「預標準化」和標準化之間改變。 – bolov

+0

檢查14.6.4.1 [temp.point]規則 – AndyG

+0

也搜索兩個階段名稱查找 – bolov

回答

13

「1985年,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++中有臭名昭著的兩相名字查找:

  • 在第一階段,在模板定義,所有non-dependent names已解決。不這樣做是一個嚴重的錯誤;
  • 在模板實例化中,相關名稱在階段2中解析。

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

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. bring gT
template <class T> 
void f(T) 
{ 
    T::g(); 
} 

struct X { 
    static void g(); 
}; 

int main() 
{ 
    X x; 
    f(x); 
} 
  • 經由ADL帶上gT
  • 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 Argument-dependent name lookup [basic.lookup.argdep]

    1. For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered [...]:

      • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

      • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members. [...]


    最後我們得到爲什麼extern void g(double);裏面主要是沒有發現:首先我們發現, g(fundamental_type)被發現iff它在f定義之前被聲明。所以讓我們在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 : error: no member named 'g' in the global namespace; did you mean simply 'g'?

    +0

    'h'應該是無效的,我糾正它。 – user1289

    +0

    謝謝你的回答。我有兩個問題。 1.爲什麼在第一個例子中'g'是一個非依賴的?它依賴於T(在本例中爲int),使用ADL它可以在命名空間T中找到(對於內置類型命名空間應該是全局命名空間,據我所知)。 2.用你的邏輯,爲什麼'f(x)'有效,但是'f(2)'不能和ADL一起工作? – user1289

    +1

    @ user1289:'f(2)'不適用於ADL,因爲'2'是'int',這是一個基本類型,而「1)對於基本類型的參數,相關的一組名稱空間和類是空的「(請參閱3.9.1基本類型[basic.fundamental]) – AndyG