2013-02-10 17 views
14

Mam ten prosty kod:specjalizacja szablonu i klas pochodnych w 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); 
} 

Kiedy go uruchomić (VS2010) Mam tego wyjścia:

specialization 
template 
template 

Czy to możliwe, aby połączenia z A-derived zajęcia do wykorzystania specjalizacji?

+0

Co dokładnie masz na myśli? Pytanie jest całkowicie niejednoznaczne. –

+2

@Desolator Oznacza on, że chce, aby wszystko, co pochodzi od A, jak również z instancji A, wykorzystywało przesłonięcie do odniesienia A, a nie ogólne rozwinięcie szablonu. Jego wezwania do używania 'foo()' z instancjami 'B' i' C' używają generycznego, a nie przesłonięcia. – WhozCraig

+0

@WhozCraig Dzięki! Właśnie tego chcę. Klasy pochodne A mają funkcję, którą muszę wywołać. Inne typy nie mają tej funkcji i zamiast tego muszę użyć cech. – Felics

Odpowiedz

20

Tak, jest to możliwe, ale trzeba trochę zmienić kod.

Przede wszystkim, aby mieć charakter techniczny, druga funkcja f() jest nie specjalizacja funkcji szablonu, ale przeciążenia . Podczas rozwiązywania przeciążenia wersja szablonu jest wybierana dla wszystkich argumentów, których typ nie jest A, ponieważ jest idealnie dopasowany: T jest wyprowadzony jako równy typowi argumentu, więc podczas wywoływania f(b), na przykład, po odliczeniu typu, kompilator będzie musiał wybrać między dwoma następującymi przeciążeniami:

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

Oczywiście pierwszy jest lepszy.

Teraz, jeśli chcesz wybrać drugą wersję, gdy funkcja jest wywoływana z argumentem będącym podklasą A, musisz użyć pewnej techniki SFINAE, aby uniemożliwić prawidłowe tworzenie szablonu funkcji, gdy typ T jest wywnioskować, że jest podklasą A.

Możesz użyć std::enable_if w połączeniu z cechami typu std::is_base_of, aby to osiągnąć.

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

Oto jak można go używać w pełnym programie:

#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:

Jeśli pracujesz z kompilatora, który nie jest w pełni zgodny z C++ 11 (i dlatego nie obsługuje domyślnych szablonów argumentów na szablonach funkcji), możesz zmienić definicję przeciążenia szablonu na f() w następujący sposób:

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

Zachowanie programu będzie identyczne. Zwróć uwagę, że jeśli typem powrotu jest f() jest void, możesz pominąć drugi argument w szablonie klasy enable_if.

+0

+1 do ciebie Andy. Już miałem zacząć pisać odpowiedź SFINAE na to, dzięki za zapisanie mi klawiszy =) – WhozCraig

+0

@WhozCraig: Dziękuję :-) –

+0

Uwaga: nazwa 'p' jest opcjonalna na liście parametrów szablonu. –

Powiązane problemy