2016-10-10 13 views
20

Kod ten jest z "C++ język programowania" przez Bjarne Stroustrup (C.13.8.3 Punktu instancji wiązaniami)W którym momencie występuje powiązanie tworzenia instancji szablonu?

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

void g(int v); 

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

I wspomina:

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 
} 

Moje pytania są następujące:

  1. Dlaczego powinien działać pierwszy kod? g() jest zadeklarowany później i naprawdę dostaję błąd z G ++ 4.9.2, że g nie jest zadeklarowany w tym punkcie.

  2. extern g (podwójne) - jak to działa? skoro wartość zwracana nie ma znaczenia w przypadku przeciążenia funkcji, możemy ją pominąć w deklaracjach forward?

  3. Punkt powstawania dla f() jest tuż przed h() - dlaczego? czy nie jest logiczne, że zostanie wywołany podczas wywoływania f(2)? Tam, gdzie to nazwiemy, skąd g(double) będzie już w zasięgu.

  4. Definicja "punktu tworzenia instancji" oznacza, że ​​parametr szablonu nigdy nie może być powiązany z lokalną nazwą lub członkiem klasy - Czy zmieniło się to w C++ 14? Dostaję błąd z C++ (G ++ 4.9.2), ale nie dostaję błędu w C++ 14 (G ++ 4.9.2).

+2

„w 1985 roku, pierwsza edycja C++ Programming Language został wydany, które stały się ostateczne odniesienie do języka, jak ** nie było jeszcze oficjalny standard **. " [wiki] (https://en.wikipedia.org/wiki/C%2B%2B#History) Tak więc nie zmieniło się pomiędzy 'C++ 11' i' C++ 14'. Zmieniło się pomiędzy "standaryzacją" a standaryzacją. – bolov

+0

Sprawdź 14.6.4.1 [temp. Punktu] dla zasad – AndyG

+0

szukaj również wyszukiwania z dwoma fazami – bolov

Odpowiedz

13

„W 1985 roku, pierwsza edycja C++ Programming Language został wydany, które stały się ostateczne odniesienie do języka, jak nie było jeszcze oficjalnym standardem”. wiki C++ History Nie zmieniło się to między C++ 11 i C++ 14. Mogę założyć (i proszę przyjąć to z przymrużeniem oka), które zmieniło się między "standaryzacją" a standaryzacją. Może ktoś, kto wie lepiej historię C++, może rzucić więcej światła tutaj.

chodzi o to, co rzeczywiście się dzieje:


Najpierw przejdźmy się na sposób prosty:

extern g(double); 

To nieważne C++. Historycznie, niestety C dozwolone pominięcie typu. W C++ musisz napisać extern void g(double).


Następnie zignorujmy przeciążenie g(double) odpowiedzieć na pierwsze pytanie:

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

void g(int v); 

int main() 
{ 
    f(2); 
} 

W C++ jest niesławny dwufazowy wyszukiwanie nazw:

  • W pierwszej fazie, w definicja szablonu, wszystkie non-dependent names zostały rozwiązane. Niewykonanie tego jest trudnym błędem;
  • Nazwy zależne są rozwiązywane w fazie drugiej podczas tworzenia szablonu.

Zasady są nieco bardziej skomplikowane, ale to jest sedno.

g jest zależny od parametru szablonu T, więc przechodzi przez pierwszą fazę. Oznacza to, że jeśli nigdy nie tworzysz instancji f, kod kompiluje się dobrze. W drugiej fazie f jest tworzony z T = int. g(int) jest teraz szukał, ale nie znaleziono:

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); 

W porządku dla dowolnej nazwy g zdać śpiewająco mamy kilka możliwości:

  1. Zadeklaruj g poprzednio:
void g(int); 

template <class T> 
void f(T value) 
{ 
    g(value); 
} 
  1. przynieśsię z T:
template <class T> 
void f(T) 
{ 
    T::g(); 
} 

struct X { 
    static void g(); 
}; 

int main() 
{ 
    X x; 
    f(x); 
} 
  1. Doprowadzić g się z T poprzez ADL:
template <class T> 
void f(T value) 
{ 
    g(value); 
} 

struct X {}; 

void g(X); 

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

Te oczywiście zmiany semantyki programu. Mają one ilustrować, co możesz i czego nie możesz mieć w szablonie.


Jak, dlaczego nie znaleźć g(int) ADL, ale znajdzie 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. [...]


I wreszcie dostać się do wewnątrz dlaczego extern void g(double); główny nie zostanie znaleziony: przede wszystkim pokazaliśmy, że g(fundamental_type) został znaleziony iff jest zadeklarowany przed definicją f. Zróbmy więc void g(X) wewnątrz main. Czy ADL ją odnajduje?

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

struct X{}; 


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

    f(x); 
} 

Nie, ponieważ nie znajdują się w tej samej przestrzeni nazw jako X (tj globalnej przestrzeni nazw) ADL nie może go znaleźć.

Dowód g nie jest w globalnej

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' powinno być nieważne, poprawiłem je. – user1289

+0

Dziękuję za odpowiedź. Mam dwa pytania. 1. Dlaczego w pierwszym przykładzie 'g' jest niezależnym? Jest zależny od T (w tym przypadku int), z ADL można go znaleźć w przestrzeni nazw T (dla wbudowanych typów przestrzeń nazw ma być globalną przestrzenią nazw, o ile wiem). 2. Z twoją logiką, dlaczego 'f (x)' działa, ale 'f (2)' nie działa z ADL? – user1289

+1

@ user1289: 'f (2)' nie działa z ADL, ponieważ '2' to' int', który jest typem podstawowym, i "1) Dla argumentów typu podstawowego, powiązany zestaw obszarów nazw i klas jest pusty "(Zobacz 3.9.1 Typy podstawowe [podstawowe.fundamentowe]) – AndyG

Powiązane problemy