24 listopada 2013

[C++11] Budowaniu szablonów

Szablony i ich specjalizacje powinny być deklarowane w tym samym pliku nagłówkowym. Na początku powinny wystąpić wszystkie szablony ogólne, a następnie ich specjalizacje. Możemy częściowo wyspecjalizować tylko klasę. Nie można częściowo wyspecjalizować funkcji szablonowej [1].

Wewnątrz klasy szablonowej kompilator traktuje referencję do tego samego szablonu tak jakby był podany argument dla tego szablonu.
#include <iostream>
using namespace std;

template <typename T>
struct Single {
    Single& me_one() {
        return *this;
    }
    Single<T>& me_two() {
        return *this;
    }
    std::string show() {
        return "chain call";
    }
};

int main() {
    Single<int> s;
    cout << s.me_one().me_two().me_two().show() << endl;
    return 0;
}
Wynik:
chain call
W nowym standardzie, możemy uczynić typ parametryczny przyjacielem klasy.
#include <iostream>
using namespace std;

template <typename Type>
struct Bar {
    friend Type;
protected:
    std::string show() {
        return "protected show()";
    }
};

struct FriendType {
    void run(Bar<FriendType>& b) {
        cout << b.show() << endl;
    }
};

int main() {
    FriendType fry;
    Bar<FriendType> bar;
    fry.run(bar);
    return 0;
}
Wynik:
protected show()
Kompilator domyślnie zakłada że nazwa do której się odwołujemy przez operator zakresu (namespace) nigdy nie jest typem dlatego w takich przypadkach należy używać słowa typename.
template <typename T>
typename T::value_type fun(const T& c) {
    return c.res();
}
Wczesne wersje standardu dopuszczały domyślne parametry tylko dla szablonów klasy, w nowym standardzie, można stosować je także dla funkcji.
#include <iostream>
using namespace std;

template <typename T, typename F = less<T> >
int cmp(const T& a, const T&b) {
    F func = F();
    return func(a, b);
}

int main() {
    cout << cmp(1, 3) << endl;
    cout << cmp<int, greater<int>>(1, 3) << endl;
    return 0;
}
Wynik:
1
0
W dużych systemach, koszt inicjowania tego samego szablonu w wielu plikach, może być bardzo duży. Nowy standard pozwala uniknąć tego narzutu dzięki "explicit instantiation" ~ jednoznaczna konkretyzacja. Kiedy kompilator napotka deklarację szablonu jako extern, nie wygeneruje kodu dla tej konkretyzacji w danym pliku - taka deklaracja obiecuje, że gdzieś w programie będzie użyta non-extern konkretyzacja. Może być kilka deklaracji typu extern, ale zawsze musi być jedna definicja dla tej konkretyzacji.
// kompilator nie wygeneruje kodu w tym pliku
extern template class Blob<string>;
// kod tej szablonowej metody zostanie wygenerowany w obecnym pliku .o
template int compare(const int&, const int&);

Brak komentarzy:

Prześlij komentarz