2012-04-20 26 views
16
#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

struct B : public A { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B b; 
    b.call(); 
    return 0; 
} 

Daje expected result:Kolejność wyszukiwania symboli C++ dla szablonu i klasy innej niż szablon?

A::foo() 

Jednak po zmianie dwie linie (klasa B do szablonu):

#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

template <typename T> // change here 
struct B : public T { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B<A> b; // and here 
    b.call(); 
    return 0; 
} 

uzyskać unexpected result:

global foo() 

i korzystania this-> jest nie jest opcją, ponieważ próbuję stworzyć zjadł mechanizm "awaryjny".

+0

Co ciekawe. –

+0

Czy 'call() {T :: foo(); } 'być w porządku, aby użyć? To działa. – chris

+0

Co powiesz na wywołanie 'T :: foo();' z 'B :: call'? – mfontanini

Odpowiedz

16

To, co otrzymasz, jest oczekiwanym rezultatem. Jest to nazywane "Wyszukiwanie dwufazowe" w standardzie C++.

Nazwy wewnątrz szablonów są podzielone na dwa rodzaje:

Zależne - Nazwy, które zależą od parametrów szablonu, ale nie są zadeklarowane w szablonie.

Niezależne - nazwy, które nie zależą od parametrów szablonu, a także nazwy samego szablonu i zadeklarowanych w nim nazw.

Gdy kompilator próbuje rozwiązać niektóre nazwy w kodzie, najpierw decyduje, czy nazwa jest zależna, czy nie, a proces rozstrzygania wynika z tego rozróżnienia. Podczas gdy niezależne nazwy są rozwiązywane "normalnie" - po zdefiniowaniu szablonu rozdzielczość dla nazw zależnych dzieje się w punkcie tworzenia szablonu.

w B::call w twoim przykładzie jest niezależną nazwą, więc jest rozstrzygnięty na globalną foo() w punkcie definicji szablonu.

+0

Jak to działa z komentarzem Jagannatha: "Działa dobrze, VS 11 Beta. Wywołuje A :: foo()."? Wiem, że MS nie jest najlepszą rzeczą do sprawdzenia jakichkolwiek standardów przeciwko. Również wszelkie odniesienia do standardu C++ (03) byłoby miłe. Czy wiesz, jak sprawić, by zachowywała się tak, jak chcę? – elmo

+3

@elmo: Microsoft C++ nie implementuje poprawnie dwuetapowego sprawdzania nazw. Zobacz [to pytanie] (http://stackoverflow.com/questions/6273176). –

+0

@elmo: Żeby było pracować na swój sposób: szablonu struct B: T {public void call() { T :: foo(); // dodaj T :: kodowi } }; – CppLearner

0

Musisz wyraźnie powiedzieć, aby użyć metody klasy T.

template <typename T> 
struct B : public T { 
    void call() 
    { 
     T::foo(); 
    } 
}; 


Ale jeśli chodzi o mechanizm awaryjnej, można sprawdzić to pytanie: Is it possible to write a template to check for a function's existence?

Korzystanie awaria Zmiana nie jest błąd (SFINAE), można sprawdzić dla metody foo w T, a następnie uruchom właściwą metodę.

+0

Proszę zobaczyć jego komentarz i ostatnią linijkę jego pytania. – Jagannath

+0

Jeszcze raz: ostatnia linia w moim pytaniu stwierdza wyraźnie, że nie zadziałałaby: http://ideone.com/YuoIH – elmo

+0

@elmo: dlaczego ty oprócz tego działa, gdy struktura '' C'' nie ma metody '' foo''? http://ideone.com/WcLQE – fogbit

3

Przyjęta odpowiedź wyjaśnia, dlaczego widzisz takie zachowanie, ale nie wiesz, jak uzyskać pożądane zachowanie "awaryjne". Można to zrobić za pomocą SFINAE, wprowadzając parę przeciążeń szablonu elementu, z których jedna będzie istnieć tylko wtedy, gdy klasa podstawowa ma funkcję składową o nazwie foo.

template <typename T> 
struct B : T { 
    template <void (T::*)()> struct has_mem_fn {}; 

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();} 
    template <typename U> void call(...) {foo();} 

    void call() {call<T>(0);} 
}; 

struct X {}; 

int main() 
{ 
    B<A> ba; 
    ba.call(); // A::foo() 

    B<X> bx; 
    bx.call(); // global foo() 
} 

UPDATE: Właśnie zauważyłem swoje komentarze w innej odpowiedzi, gdzie można powiedzieć, że jesteś świadomy tej metody, ale nie można go używać z powodu konieczności wspierania dysfunkcyjnych kompilatorów. W takim przypadku obawiam się, że to, czego chcesz, jest prawdopodobnie niemożliwe.

Powiązane problemy