2012-01-21 24 views
6
#include <iostream> 
template <class T> 
void foo(T) { 
    std::cout << "foo(T)" << std::endl; 
} 

template <class T> 
void foo(T*) { //#3 
    std::cout << "foo(T*)" << std::endl; 
} 

#define TEST 

#ifdef TEST 
template <> 
void foo(int*) { //#1 
    std::cout << "foo(int*)" << std::endl; 
} 
#else 
template <> 
void foo<int*>(int*) { //#2 
    std::cout << "foo<int*>(int*)" << std::endl; 
} 
#endif 

int main(int argc, char **argv) { 
    int* p = 0; 
    foo(p); 
    return 0; 
} 

jaka jest różnica między # 1 i # 2. Jeśli zdefiniuję TEST, # 1 działa. Ale jeśli skomentuję to, # 3 pracuję ... A jaki jest właściwy sposób pisania specjalizacji szablonów funkcji ...Specjalizacja szablonów funkcji nie powiodła się?

Odpowiedz

3

# 1 deklaruje funkcję szablonu specjalizacji nr 3 i automatycznie rozpoznaje parametry szablonu. # 2 to specjalizacja pierwszego zdefiniowanego przez ciebie szablonu (ten bez numeru, nazwijmy go # 0) dla T=int*. Nie może to być specjalizacja nr 3, ponieważ zastąpienie T określonym parametrem int* doprowadziłoby do parametru int**.

Po wywołaniu foo, rozdzielczość przeciążania najpierw wybiera najlepiej pasujący szablon podstawowy, a następnie sprawdza ten szablon pod kątem istniejących specjalizacji. Po zdefiniowaniu TEST istnieją dwa szablony podstawowe (# 0 i # 3), a nr 3 jest lepiej dopasowany i zostaje wybrany. Następnie kompilator sprawdza specjalizacje tego szablonu, a numer 1 jest lepiej dopasowany i jest wywoływany.

Bez zdefiniowanego TEST, istnieją jeszcze dwa szablony podstawowe (# 0 i # 3), a nr 3 jest lepiej dopasowany i zostaje wybrany.Następnie kompilator sprawdza specjalizacje tego szablonu, ale ponieważ # 2 specjalizuje się w # 0, a nie # 3, nie jest on brany pod uwagę i kończy się na # 3.

To jest klasyczny przykład Why not Specialize Function Templates. Problemy wyjaśniono tam bardziej szczegółowo.

Prostym rozwiązaniem jest nie specjalizują się szablony funkcyjne w ogóle, ale po prostu dodać nowe przeciążeń dla specjalnych typów:

// no template, just a normal function 
void foo(int*) { 
    std::cout << "foo(int*)" << std::endl; 
} 
0

Nie mogę powiedzieć, która funkcja # 2 ma się specjalizować, ani dokładnie, w jaki sposób bardzo skomplikowane reguły dotyczące przeciążania wybierałyby funkcję, którą należy wywołać.

Wiem, że najczęściej nie musisz potrzebujesz, aby specjalizować funkcje, ale możesz polegać na przeciążeniu. Aby uzyskać funkcję int* wystarczy

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

Funkcja non-template preferowane będą nad szablonów, o ile odpowiada parametr.

+0

Istnieją pewne konteksty, gdzie jest to wygodne lub niezbędne do określenia argumentu szablon (y) . Oczywistym miejscem, w którym należy podać argumenty, jest to, czy argument szablonu nie jest wydedukowany. Miejsca, w których jest to wygodne, to kiedy musisz przekierować argument do odpowiednio skonwertowanego typu. Jeśli chcesz jawnie określić argument, często nie możesz użyć przeciążenia. –

2

W przypadku specjalizacji szablonów funkcji można jawnie wymienić argumenty szablonu, ale nie trzeba, jeśli wyprowadzono argumenty szablonu. Jeśli nie określisz argumentów szablonu, zostaną one wyprowadzone przez kompilator przy użyciu tych samych reguł, co rozdzielczość przeciążania. W celu podjęcia decyzji, które przeciążenie funkcji ma zostać wybrane, kompilator zaczyna od patrzenia tylko na podstawowe szablony (które są wybierane przez jakiś magiczny proces w pierwszej kolejności). Patrząc na dwa dostępne podstawowe szablony, ten ostatni lepiej pasuje do argumentu wskaźnika. Po znalezieniu właściwego szablonu podstawowego kompilator wyszukuje potencjalne specjalizacje tego podstawowego szablonu. Jednak Twój przykład nr 2 w rzeczywistości nie jest specjalizacją szablonu funkcji przy podejmowaniu argumentu wskaźnika, chociaż zawiera argument wskaźnika. Zażycie Podstawowym Deklarację z

template <typename T> void foo(T*); 

i zastąpić T przez wyraźnie określonego szablonu argumentu int* masz

template <> void foo<int*>(int**); 

Oznacza to, że oświadczenie

template <> void foo<int*>(int*); 

jest coś innego. Prawdopodobnie po prostu chcą stracić wskaźnik przy określaniu argumentu szablonu:

template <> void foo<int>(int*); 
+1

Dlaczego mówisz, że '# 2' nie powinno się kompilować? Pasuje do pierwszego szablonu, 'template void foo (T)' z 'T = int *'. –

+0

@AaronMcDaid Oh, przepraszam, masz rację: nie widziałem, że istnieją dwa podstawowe szablony i pytanie, które z nich jest używane. Ponieważ jednak wersja wskaźnika podstawowego dopasowania jest lepszym wyborem, szablon ten zostanie wybrany, nawet jeśli jego specjalizacja lepiej pasuje: rozdzielczość przeciążania działa w odniesieniu do szablonów podstawowych. Po wybraniu szablonu funkcji wybrana jest właściwa specjalizacja. Chyba powinienem edytować odpowiedź, aby to odzwierciedlić ... –

Powiązane problemy