2013-02-10 14 views
14

ho questo semplice codice:specializzazione Template e classi derivate in C++

class A{}; 
class B : public A{}; 
class C : public B{}; 

class Test 
{ 
    public: 
     template<typename T> 
     void f(T&){printf("template\n");} 
     void f(A&){printf("specialization\n");} 
}; 

int main() 
{ 
    A a; 
    B b; 
    C c; 

    Test test; 
    test.f(a); 
    test.f(b); 
    test.f(c); 
} 

quando l'eseguo (VS2010) ho questa uscita:

specialization 
template 
template 

E 'possibile effettuare le chiamate con A-derived classi per utilizzare la specializzazione?

+0

Cosa intendi esattamente? La domanda è totalmente ambigua –

+2

@Desolator Significa che vuole che tutto ciò che deriva da A e le istanze A stesse usino l'override per un riferimento A piuttosto che per l'espansione del modello generico. Le sue chiamate a usare 'foo()' con istanze 'B' e' C' stanno usando il generico, non l'override. – WhozCraig

+0

@WhozCraig Grazie! Questo è esattamente quello che voglio. Le classi derivate da A hanno una funzione che devo chiamare. Gli altri tipi non hanno questa funzione e ho bisogno di usare invece le caratteristiche. – Felics

risposta

20

Sì, è possibile, ma è necessario modificare il codice un po '.

Prima di tutto, essere tecnico, la seconda funzione è f()non una specializzazione del modello di funzione, ma un sovraccarico. Quando si risolve il sovraccarico, la versione del modello viene scelta per tutti gli argomenti il ​​cui tipo non è A, perché è una corrispondenza perfetta: T viene dedotto per essere uguale al tipo dell'argomento, quindi quando si chiama f(b), ad esempio, dopo la deduzione di tipo il il compilatore dovrà scegliere tra i seguenti due sovraccarichi:

void f(B&){printf("template\n");} 
void f(A&){printf("specialization\n");} 

Naturalmente, il primo è una corrispondenza migliore.

Ora se si desidera selezionare la seconda versione quando si richiama la funzione con un argomento che è una sottoclasse di A, è necessario utilizzare una tecnica SFINAE per impedire che il modello di funzione venga istanziato correttamente quando il tipo T è dedotta per essere una sottoclasse di A.

È possibile utilizzare std::enable_if in combinazione con i tratti del tipo std::is_base_of per ottenere ciò.

// This will get instantiated only for those T which are not derived from A 
template<typename T, 
    typename enable_if< 
     !is_base_of<A, T>::value 
     >::type* = nullptr 
    > 
void f(T&) { cout << "template" << endl; } 

Ecco come si usa in un programma completo:

#include <type_traits> 
#include <iostream> 

using namespace std; 

class A{}; 
class B : public A{}; 
class C : public B{}; 
class D {}; 

class Test 
{ 
    public: 

     template<typename T, 
      typename enable_if<!is_base_of<A, T>::value>::type* = nullptr 
      > 
     void f(T&) { cout << ("template\n"); } 

     void f(A&){ cout << ("non-template\n");} 

}; 

int main() 
{ 
    A a; 
    B b; 
    C c; 
    D d; 
    float f; 

    Test test; 
    test.f(a); // Will print "non-template" 
    test.f(b); // Will print "non-template" 
    test.f(c); // Will print "non-template" 
    test.f(d); // Will print "template" 
    test.f(f); // Will print "template" 
} 

EDIT:

Se si sta lavorando con un compilatore che non è completamente compatibile con C++ 11 (e quindi non supporta gli argomenti del modello predefinito sui modelli di funzione), potrebbe essere necessario modificare la definizione del sovraccarico del modello di f() come segue:

template<typename T> 
typename enable_if<!is_base_of<A, T>::value, void>::type 
f(T&) { cout << ("template\n"); } 

Il comportamento del programma sarà identico. Notare che se il tipo restituito di f() è void, è possibile omettere il secondo argomento del modello di classe enable_if.

+0

+1 a te Andy. Stavo per iniziare a digitare la risposta di SFINAE, grazie per avermi salvato le sequenze di tasti =) – WhozCraig

+0

@WhozCraig: Grazie :-) –

+0

Nota: il nome 'p' è facoltativo nell'elenco dei parametri del modello. –

Problemi correlati