2010-02-01 13 views
13

Czy ktoś może podsumować pomysł przeciążenia szablonu funkcji? Co jest ważne, parametr szablonu lub parametr funkcji? A co z wartością zwracaną?Szablon funkcji przeciążenie

Na przykład, biorąc pod uwagę szablon funkcja

template<typename X, typename Y> void func(X x, Y y) {}; 

co jest przeciążony szablon funkcja?

1) template<typename X> void func(X x, int y) {};  
2) template<typename X, typename Y> X func(X x, Y y) {};  
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {}; 
+13

Wszystkie poprzedzające znaki ';' są bezcelowe. Nie są one wymagane i powodują, że patrzenie na nie jest bardziej mylące. – Omnifarious

Odpowiedz

21

Z tej listy tylko druga wprowadza niejednoznaczność, ponieważ funkcje - bez względu na to, czy są szablonami - nie mogą być przeciążane w oparciu o typ zwracany.

Można użyć dwóch pozostałych:

template<typename X> void func(X x, int y); 

zostaną wykorzystane, jeżeli drugi argument wywołania jest int, np func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z); 

zostaną wykorzystane jeśli zadzwonisz func z trzech argumenty.

Nie rozumiem, dlaczego niektóre inne odpowiedzi wspominają, że funkcje szablonu i przeciążanie funkcji nie mieszają się. Z pewnością tak, i istnieją specjalne zasady, w jaki sposób wybrana jest funkcja wywołania.

14.5.5

A function template can be overloaded with other function templates and with normal (non-template) functions. A normal function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.)

Non-matrycy (lub "mniejszej matrycy") przeciążenie korzystnie szablonów, np

template <class T> void foo(T); 
void foo(int); 

foo(10); //calls void foo(int) 
foo(10u); //calls void foo(T) with T = unsigned 

Twój pierwszy przeciążenie z jednego parametru bez szablonu również podlega tej zasadzie.

Biorąc pod uwagę wybór między kilkoma szablonami, preferowane są bardziej wyspecjalizowane mecze:

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

int i; 
int* p; 
int arr[10]; 

foo(i); //calls first 
foo(p); //calls second 
foo(arr); //calls second: array decays to pointer 

można znaleźć bardziej formalny opis wszystkich reguł w tym samym rozdziale Standard (szablony funkcyjne)


I w końcu są sytuacje, w których dwa lub więcej przeciążeń byłoby niejednoznaczne:

template <class T> void foo(T, int); 
template <class T> void foo(int, T); 

foo(1, 2); 

Tutaj połączenie jest niejednoznaczne, ponieważ obaj kandydaci są jednakowo wyspecjalizowani.

Możesz rozróżnić takie sytuacje za pomocą (na przykład) boost::disable_if. Na przykład, możemy określić, że gdy T = int, potem drugi przeciążenie nie powinny być włączone jako kandydata przeciążeniem:

#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits/is_same.hpp> 
template <class T> 
void foo(T x, int i); 

template <class T> 
typename boost::disable_if<boost::is_same<int, T> >::type 
foo(int i, T x); 

foo(1, 2); //calls the first 

Tu biblioteka produkuje „niepowodzenie podstawienie” w zwracanego typu drugiego przeciążeniem , jeśli T = int, usuwając go z zestawu przeciążonych kandydatów.

W praktyce rzadko należy wpadać w takie sytuacje.

+0

Dzięki za poprawkę! – Potatoswatter

+0

Zauważ, że dla 'szablonu void func (T); template void func (T *); 'możliwe jest również wykonanie wersji, która obsługuje specyficzne tablice:' template void func ((T &)[N]); 'Zapobiega to rozkładowi macierzy do wskaźnika i wywołaniu –

+0

Próbuję użyć 'template class_name :: class_name (const T &);' i 'template class_name :: class_name (const T *);' aby utworzyć instancję obiektu klasy, ale kiedy tylko przejdę char array próbuje wywołać 'class_name (const T &)' zamiast 'class_name (const T *)'. Otrzymuję komunikat o błędzie include/Class_name.h | 67 | error: brak pasującej funkcji dla wywołania 'to_string (const char [43]) "| ponieważ, w' class_name (const T &) 'Używam' to_string (T) 'podczas gdy w' class_name (const T *) 'Przekazuję specjalnie tablice znaków char. –

1

Stoję poprawione - patrz komentarze poniżej. Nie zmienię żadnego z moich oryginalnych postów, ponieważ spowoduje to usunięcie kontekstu odpowiedzi. Dziękuję komentujących za ich wkład i za tak miły i nie głosować mnie


Rozważmy templating być jak makra pre-procesor, który rozszerza #defines przed kompilator pobiera je zobaczyć.

Kompilator "rozszerzy" parametry twojego szablonu, a następnie zajrzy do deklaracji funkcji. Tak więc parametr szablonu == parametr funkcji. Jeśli dwukrotnie zadeklarujesz tę samą funkcję, pojawi się błąd.

Pytasz o typ zwrotu. Jest to część "podpisu" funkcji. Dwie funkcje o tych samych parametrach, ale różnych typach powrotów, to dwie różne funkcje.

+4

Typ powrotu funkcji nemplate jest _nie_ częścią jej podpisu (patrz http://stackoverflow.com/questions/290038/is-tre-return-part-of-the-function-signature/290048#290048). –

+1

Porównanie z makrami preprocesora jest raczej wadliwe. Istnieją specjalne zasady dotyczące przeciążeń. Jeśli masz 'template void foo (T);' i 'void foo (int);' wtedy wywołanie takie jak 'foo (3);' z pewnością rozwiąże ten drugi (nie szablony są preferowane w stosunku do szablonów, jeśli pasują do siebie dokładnie). – UncleBens

+0

jeśli typ zwrotu był częścią signiture, można byłoby przeciążać na typ retrun, którego nie można –

3

Są tu dwie odrębne rzeczy: funkcja szablonowania i przeciążanie funkcji. Jakiekolwiek dwa różne deklaracje szablonów prawdopodobnie będą przeciążać się nawzajem, więc twoje pytanie nie ma sensu, jak stwierdzono. (Trzy "przeciążenia", które podajesz, nie opierają się na pierwszym szablonie, a zamiast tego masz cztery przeciążenia do tej samej nazwy funkcji.) Prawdziwym problemem jest, biorąc pod uwagę przeciążenia i wywołanie, jak wywołać pożądane przeciążenie?

Po pierwsze, typ zwracany nie bierze udziału w procesie przeciążania, niezależnie od tego, czy istnieje szablon. WięC# 2 nigdy nie zagra dobrze z numerem 1.

Po drugie, zasady dotyczące rozdzielczości przeciążenia szablonem funkcji różnią się od najczęściej używanych reguł specjalizacji szablonów klas. Zarówno zasadniczo rozwiązać ten sam problem, ale

  • zasady szablonów klas są prostsze i bardziej wydajne, co pozwala na przykład rekursji i funkcje (członek) różniące się tylko przez typ zwracany
  • zasady szablonów funkcyjnych pozwalają kompilator zrozumieć argumenty szablon z typów argumentów funkcja

może być w stanie rozwiązać konkretny problem z przeciążeniem szablon funkcji, ale może masz problemy mocującą dowolny błąd, który powstaje jako reguły są dłuższe i mniej osób znają ich zawiłości. Po kilku latach hackowania szablonów nie zdawałem sobie sprawy, że subtelne przeciążanie szablonu funkcji było nawet możliwe. W bibliotekach takich jak Boost i GCC's STL alternatywne podejście jest wszechobecne. Użyj szablonowej klasy opakowania:

template< typename X, typename Y > 
struct functor { 
    void operator()(X x, Y y); 
}; 
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes 
    void operator()(X x, int y); 
}; 

Teraz poświęcasz ukrytą składnię tworzenia instancji (bez nawiasów ostrych).Jeśli chcesz dostać to z powrotem, trzeba innej funkcji

template< typename X, typename Y > void func(X x, Y y) { 
    return functor< X, Y >()(x, y); 
} 

byłbym zainteresowany, aby usłyszeć, czy funkcja przeciążenia może nic zrobić (oprócz odliczenia), że klasa [częściowy] specjalizacja nie mogę ...

I wtedy, oczywiście, twoje przeciążenie # 3 nigdy nie stanie się niejednoznaczne, ponieważ ma inną liczbę argumentów niż jakiekolwiek inne przeciążenie.

+0

+1 za pracę nad częściową specjalizacją. –

+0

Twój przykład nie wydaje się być wieloznaczny. Wywołanie pasuje do drugiego (jeden parametr dokładnie pasuje do int). Być może chodziło o przeciążenia typu: 'template void func (int x, X y); template void func (X x, int y); ' – UncleBens

+0

Drugi' func' odrzuci niektóre z argumentów zaakceptowanych przez pierwsze 'func', ale nie odwrotnie, co czyni szablon bardziej wyspecjalizowanym. Dla zwykłego wywołania funkcji, gdybyśmy zignorowali tę kolejność, wywołanie byłoby niejednoznaczne, ponieważ parametr szablonu jest również dedukowany do 'int' podobnie. –