2012-04-20 10 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; 
} 

Questo dà expected result:Ordine di ricerca del simbolo C++ diverso per modello e classe non modello?

A::foo() 

Tuttavia dopo aver cambiato due linee (classe B al modello):

#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; 
} 

ottengo unexpected result:

global foo() 

E usando this-> è non è un'opzione come sto cercando di creare mangiato un meccanismo di "fallback".

+0

Interessante davvero. –

+0

Would 'call() {T :: foo(); } 'be ok da usare? Funziona. – chris

+0

Che ne dici di chiamare 'T :: pippo();' da 'B :: call'? – mfontanini

risposta

16

Quello che ottieni è un risultato previsto. Questo è chiamato "Ricerca del nome in due fasi" nello standard C++.

nomi all'interno mascherine sono divisi in due tipi:

dipendenti - nomi che dipendono i parametri di modello, ma non dichiarati all'interno del modello.

Non dipendente - nomi che non dipendono dai parametri del modello, oltre al nome del modello stesso e dei nomi dichiarati al suo interno.

Quando il compilatore tenta di risolvere qualche nome nel codice, prima decide se il nome è dipendente o meno, e il processo di risoluzione deriva da questa distinzione. Mentre i nomi non dipendenti vengono risolti "normalmente" - quando il modello è definito, la risoluzione per i nomi dipendenti avviene nel momento dell'istanza del modello.

foo(); in B::call nel tuo esempio è un nome non-dipendente, per cui è deliberato di globale foo() al momento della definizione del modello.

+0

Come funziona con il commento di Jagannath "Funziona bene VS 11 Beta. Chiama A :: foo()."? So che la SM non è la cosa migliore per convalidare gli standard contro. Anche qualsiasi riferimento allo standard C++ (03) sarebbe bello. E sai come farlo comportarsi come voglio? – elmo

+3

@elmo: Microsoft C++ non implementa correttamente la ricerca di nomi in due fasi. Vedi [questa domanda] (http://stackoverflow.com/questions/6273176). –

+0

@elmo: per farlo funzionare il vostro modo: modello struct B: T pubblico { chiamata void() { T :: foo(); // Aggiungere T :: al codice }} ; – CppLearner

0

È necessario specificare specificamente per utilizzare il metodo di classe T.

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


Ma per quanto riguarda un meccanismo di fallback, è possibile controllare questa domanda: Is it possible to write a template to check for a function's existence?

utilizzando un fallimento Sostituzione non è un errore (SFINAE), è possibile controllare per un metodo foo in T, quindi eseguire il metodo corretto.

+0

Si prega di vedere il suo commento e l'ultima riga della sua domanda. – Jagannath

+0

Ancora: l'ultima riga nella mia domanda indica chiaramente che non funzionerebbe: http://ideone.com/YuoIH – elmo

+0

@elmo: perché eccetto che funziona quando la struttura '' C'' non ha il metodo '' foo''? http://ideone.com/WcLQE – fogbit

3

La risposta accettata spiega perché si vede quel comportamento, ma non come si ottiene il comportamento di "ripiego" che si desidera. Questo può essere fatto usando SFINAE, introducendo una coppia di overload di template membro, uno dei quali esiste solo se la classe base ha una funzione membro chiamata 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: Ho appena notato i tuoi commenti in un'altra risposta, in cui dici di essere a conoscenza di questo metodo, ma non posso usarlo a causa di dover sostenere compilatori disfunzionali. In tal caso, temo che ciò che vuoi sia probabilmente impossibile.

Problemi correlati