Variadic templates
• tech • 423 słowa • 2 minuty czytania
Niedawno marudziłem o makrach i funkcjach ze zmienną liczbą argumentów, więc dziś do uzupełnienia tego tematu wspomnę kilka słów o wzorcach ze zmienną liczbą parametrów, czyli Variadic templates, które niebawem staną się oficjalną częścią standardu języka C++.
Modelowa, prosta funkcja wzorcowa, która jako przykład występuje prawie w każdej wzmiance na temat “nowego ficzera”.
template<typename T>
void print(const T& t) {
std::cout << t;
}
template<typename T, typename... Args>
void print(const T& t, const Args&... args) {
print(t);
print(args...);
}
Jak można zauważyć do określenia zmienności parametrów wykorzystano znane już wyrażenie …. Konstrukcja typename… określa, że definiujemy wzorzec z nieznaną liczbą parametrów, nieznanego typu, który zostanie wydedukowany “w praniu” przez kompilator.
Parametr wzorca Args, będzie w czasie konkretyzacji wzorca rozwijany w kolejne parametry wzorca, w naszym przypadku także w argumenty funkcji.
Czyli dla wywołanie funkcji:
void f(int a, int b, int c, std::string str) {
print(a, b, c, str);
}
Kompilator “wygeneruje” funkcję wzorcową postaci:
template<typename T1, typename T2, typename T3, typename T4>
void print(const T1& t1, const T2& t2, const T3& t3, const T4& t4) {
std::cout << t1 << t2 << t3 << t4;
}
Która nastepnie zostanie skonkretyzowana dla kolejnych typów: int, int, int, std::string.
W sumie po co o tym pisze, skoro to takie oczywiste.
A co jeśli chcielibyśmy, aby argument przed przekazaniem do funkcji print został jeszcze przetworzony przez inna funkcję czy wyrażenie, aby otrzymać coś na kształt:
print(check(a), check(b), check(c));
Nic trudnego, wystarczy odpowiednio zając się argumentem:
template<typename T, typename... Args>
void print(const T& t, const Args&... args) {
print(check(t));
print(args...);
}
lub ciekawiej:
template<typename T, typename... Args>
void print(const T& t, const Args&... args) {
print(t);
print(check(args)...);
}
To teraz pora na wzorce klas ;)
Zdefiniujmy sobie prosty wzorzec klasy, który będzie służył do wyznaczania ilości parametrów wzorca:
template<>
struct count<> {
static const int value = 0;
};
template<typename T, typename... Args>
struct count<T, Args...> {
static const int value = 1 + count<Args...>::value;
};
i proste użycie:
const int args_size = count<int, int, int, std::string>::value;
Jego działanie oparte jest na zwijaniu i rozwijaniu parametrów wzorca ze zmienną liczbą parametrów (Packing and Unpacking Parameter Packs).
Najpopularniejszym zastosowaniem wzorców będzie tworzenie krotek (tuples), czyli struktur z uporządkowanym skończonym zbiorem składowych, cos jak std::pair, ale elastyczniejsze z dowolna liczbą elementów. Nie trzeba będzie się męczyć tak jak obecnie ;)
Przykładowe implementacje krotek, a także o wiele więcej ciekawych informacji, mozliwości i zastosowań Variadic templates można znaleźć w sieci. Osobiście mogę polecić stronę autora rozszerzenia Variadic Templates for GCC oraz artykuł Variadic Templates for C++0x (Douglas Gregor, Jaakko Järvi).
Komentarze (0)