Specjalizacja szablonu a zwykła definicja funkcji

0

Załóżmy, że mamy szablon:

template <typename T> void funkcja(T t);

oraz jego jawną specjalizację, np. dla double:

template <> void funkcja<double>(double t);

Moje pytanie: po co w ogóle powstała idea jawnej specjalizacji szablonu? Przecież prościej jest dopisać zwykłą definicję funkcji:

void funkcja(double t);

I na to samo wychodzi. Czegoś nie dostrzegam w tej koncepcji?

Jest to mój pierwszy post więc witam wszystkich forumowiczów :)

7

Dobre pytanie.

W zasadzie to nie wychodzi na to samo. Przeciążenia są zawsze wybierane w pierwszej kolejności, specjalizacje szablonu dopiero wtedy, kiedy bazowy szablon jest dopasowany. Może to prowadzić do zupełnie nieoczekiwanych zachowań - możesz o tym poczytać. Troszkę to stare, ale chyba nic się nie zmieniło w tej kwestii - Herb Sutter radzi tam używać przeciążeń, żeby zawsze wszystko działało tak jak chcemy.

Standard mówi, że nie można przeciążać funkcji z przestrzeni std::, jeżeli to możliwe należy używać specjalizacji. Być może ta funkcjonalność została też dodana ze względu na kompletność - skoro można specjalizować szablony klas no to funkcji też (chociaż nie można specjalizować ich częściowo, a to prowadzi właśnie do sytuacji, kiedy użycie specjalizacji jest niemożliwe). Jeżeli w danym kodzie nie mogą pojawić się wspomniane wyżej problemy, to można też powiedzieć, że to kwestia stylu - specjalizuj szablony, a zwykłe funkcje przeciążaj.

Warto dodać, że specjalizacje szablonów funkcji mogą chyba przydać się w metaprogramowaniu.

1

Jedna rzecz ktora przychodzi mi do glowy. Chodzi bardziej o ogolne uzywanie szablonow. Szablonem mozesz objac duza grupe typow obiektow przekazywanych do funkcji. Wezmy prosty przyklad.

template<class T>
void print(T path)
{
    for(int i=0;i<path.size();i++)
        cout << path[i] << ' ';
    cout << endl;
}

template<>
void print(map<int,string>path)
{
    map<int,string>::iterator it;
    for(it = path.begin(); it!=path.end();it++)
        cout << it->first << ' ' << it->second << endl;
}

int main()
{
    string a = "test";
    vector<int>b;
    map<int,string>c;
    c[1] = "test";
    c[2] = "test2";
    c[3] = "test3";
    b.push_back(1);
    b.push_back(2);
    print(a);
    print(b);
    print(c);
    return 0;
}

Przypadek uogolniony zadziala dla stringa jak i vectora tak samo dobrze, jednak juz dla mapy nie, poniewaz dziala ona inaczej, dlatego wlasnie stworzono specjalizacje, zeby dla typow szczegolnych, nie objetych szablonem moc nadal wykonywac ta sama funkcje.

0

Dzięki za wyczerpujące odpowiedzi. Pozdrawiam.

1

Jakie jest zastosowanie specjalizacji? Na przykład chcemy mieć funkcję, która rozpoznaje określony typ lub typy.

Piszemy ogólny szablon:

template <typename T> bool czyDouble(T t)
{
      return false;
}

Więc mamy taką sytuację, w której nasza funkcja łyknie każde T i zawsze zwróci false. Ale my chcemy aby rozpoznawała nam dable!
To teraz piszemy jawną specjalizację:

template <> bool czyDouble(double t)
{
      return true;
}

No i teraz jeśli zmienna będzie dablem to kompliator dopasują ją do szablonu powyżej i zwróci nam true. Tak możemy tworzyć funkcje, które rozpoznają odpowiednie typy. Bez specjalizacji tego w szablonie nie zrobimy na etapie kompilacji. Można zastosować type_id ale to już jest w trakcie wykonania.

0

@AkuAku: sprytne :) Jest to już na pewno jakieś bardziej praktyczne zastosowanie. Może znasz więcej takich przykładów?

0
dpro napisał(a):

@AkuAku: sprytne :) Jest to już na pewno jakieś bardziej praktyczne zastosowanie. Może znasz więcej takich przykładów?

Marny przykład, to wcale nie musi zostać wszystko zrobione w czasie kompilacji. Mogą zostać stworzone dwie funkcje, które zostaną normalnie wywołane podczas wykonania - @Azarien dobrze prawi.

Od tego jest type_traits!

1 użytkowników online, w tym zalogowanych: 0, gości: 1