Programowanie
C++
Variadic templates w C++0x
Michał Małecki
ariadic templates, czy też lekko karkołomnie te _ _ ((format)), który informuje kompilator, że łańcuch
tłumacząc na polski, wzorce ze zmienną li- formatujący stosuje się do reguł określonej standardo-
Vstą parametrów , to kolejna z ciekawych wła- wej funkcji (dostępne są printf, scanf, strftime i strf-
ściwości, które mają się pojawić w nowym standar- mon). W przypadku niepoprawnego wywołania kompila-
dzie C++0x. I, podobnie jak koncepcje, doczekała tor będzie stosował ostrzeżenie (np. o niewłaściwej ilo-
się nawet próbnej implementacji. Zanim przedstawię, ści argumentów lub niewłaściwych typach). To jest jed-
jak owo rozwiązanie wygląda, zacznę od problemów, nak, jak każdy wie, półśrodek (dla ciekawostki, powyż-
którym owa właściwość ma zaradzić. szego błędu w wywołaniu scanf to nie wykrywa).
Nie rozwodząc się nad tym dalej funkcja nie ma
Problem pierwszy: printf i scanf możliwości odgadnięcia, jakie argumenty zostały jej
O funkcjach o zmiennej liście argumentów w języku przekazane, jakich one są typów, ani ile ich jest. I nie
C każdy chyba słyszał (mówię w języku C , bo uży- bez powodu traktuje się ten ficzer jako naleciałość
wanie tego w C++ oznacza równocześnie rezygna- z języka C, której nie należy stosować w C++ w ogóle.
cję z niemal wszystkich zalet tego języka). Niezależ-
nie od faktycznej implementacji, najłatwiej sobie to Problem drugi call forwarding
wyobrazić jako surowy obszar pamięci, w którym po Każdy, kto zapoznał się z boost::function, boost::bind,
kolei umieszczane są obiekty podane w wywołaniu. boost::signal, libsigc++, cpptcl, czy też jakąkolwiek inną
Owe '...' w sygnaturze funkcji oznaczają jedynie, że tu biblioteką, która posługuje się przekazywaniem wywo-
lista argumentów się nie kończy, a argumenty poda- łań z jednej funkcji do drugiej (w postaci: wez te argu-
ne za ostatnim jawnym będą umieszczane w pamię- menty i wywołaj podaną funkcję, przekazując jej te ar-
ci jeden za drugim. Nie mamy jednak żadnych wska- gumenty w identycznej postaci ), zauważył że dużo pa-
zówek, jak i co z tej pamięci czytać, jeśli ich nie poda- ry idzie w pewien specyficzny gwizdek, mianowicie do-
my w jakiś sposób wprost podczas wywołania. Makra starczenie dziewięciu definicji do tego jednego najdrob-
z
ułatwiają jedynie ich zbieranie i dostar- niejszego szczegółu, który zajmuje się tym właśnie
czają kompilatorowi więcej swobody w implementa- przekazywaniem (no dobrze, boost::function dopusz-
cji ale nic ponadto. W C++, na dodatek, nie można cza nawet 50). Jest ich tyle, bo jedną definicję trzeba
w ten sposób przekazać typów innych, niż POD. dostarczyć do funkcji bez argumentów, jedną z jednym
Wezmy taką, najbardziej znaną, printf. Tam, jak argumentem, jedną z dwoma: (w którejś wersji boost::
wiemy, w napisie formatującym umieszcza się znacz- function widziałem nawet skrypt perlowy, który genero-
niki, jak np. %d , które mają określać, jakie dane na- wał wszystkie postacie z jednej definicji; obecnie boost:
leży pobrać z obszaru nieokreślonych argumentów :function używa ułatwień z Boost.Preprocessor, przez
(w tym przypadku jest to jeden obiekt typu int). Nic jed- co jest to zagmatwane jeszcze bardziej). Ile jest z tym
nak nie stoi na przeszkodzie, żeby w to miejsce podać roboty, jakie są problemy z utrzymaniem takiego kodu
np. wartość typu long double i mamy poważny problem. i ile bardziej on jest narażony na błędy o tym chyba
Z funkcją scanf z kolei jest jeszcze gorzej. Tam bar- nie trzeba nikomu przypominać.
dzo łatwo zapomnieć, że znacznikowi %d musi odpo- Powielanie jednej definicji, z uwagi na konieczność
wiadać argument &n (a nie n ), bo scanf na tej pozy- dostosowania do ilości argumentów, ma więcej wad,
cji oczekuje wskaznika do zmiennej typu int, a nie war- niż sam fakt nadmiarowej pracy. Nie można jej przecież
tości typu int. Najlepsze fajerwerki zapewnia zaś kon- powielać w nieskończoność. Wydaje się, że 9 powinno
strukcja scanf( "%1s", &c ), gdzie 'c' jest typu char wystarczyć, ale jak widzę niektóre funkcje z Windows
dzięki nadpisaniu pamięci zaraz za zmienną potrafi za- API, czy POSIX threads, to zaczynam mieć wątpliwo-
pewnić nawet nieodtwarzalność stosu wywołań. ści. Tak czy owak pożądane jest rozwiązanie, dzięki
Niektóre kompilatory próbując tym problemom któremu będzie można załatwić tę sprawę jedną, pro-
częściowo zaradzić wprowadzają rozszerzenia, któ- stą definicją. W projekcie C++0x istnieje zatem wstęp-
re sprawdzają tę poprawność. Kompilator gcc na przy- nie zaakceptowana postać takiego rozwiązania.
kład, dostarcza rozszerzenie w postaci _ _ attribu-
Variadic templates
najprościej mówiąc...
Autor interesuje się psychologią, programowaniem, mu-
Autor poprawki do gcc, a zarazem też jeden z auto-
zyką, publicystyką i językoznawstwem, a w wolnych
rów tego ficzera, Douglas Gregor, zamieścił na swo-
chwilach pracuje jako programista.
jej stronie (patrz ramka W Sieci) przykład funkcji
Kontakt z autorem: ethouris@gmail.com
printf, która używa swoich argumentów w bezpiecz-
48
www.sdjournal.org
Software Developer s Journal 05/2007
Variadic templates w C++0x
niejszy sposób (znaczniki określa się samym znakiem % ,
Listing 1. Przykładowa funkcja print z nieokreśloną
a typy i ilość argumentów są funkcji znane). Ja zaś proponuję
liczbą argumentów
coś jeszcze prostszego patrz Listing 1. Funkcji tej używa się
w następujący sposób: template
inline void print( const T& t ) {
print( "Mam ", n, " lat.\n" ); std::cout << t;
print( "Pracuję na stanowisku ", stanowisko, " i zarabiam ", }
get_secure( xkey, zarobki ), " dolców\n" ); template< typename T, typename... Args>
inline void print( const T& t, const Args&... args ) {
Co ważne, taka postać print zachowuje wszystkie zalety io- print( t );
stream operuje również obiektami, których typy nie są zna- print( args... );
ne w momencie kompilacji funkcji print. Wyjaśnienie, jak dzia- }
łają powyższe definicje, jest proste. Jeśli wywołamy print
z jednym argumentem, to zostanie użyta wersja jednoargu-
mentowa. Jeśli z dwoma, wtedy zostanie użyta wersja wielo- templates a takowe mam ambicję w tym artykule dostarczyć
argumentowa, ale wewnątrz wywoła jednoargumentową print, powinno wystarczyć do pełnego zrozumienia, z czym się to je.
a potem dokona wywołania wieloargumentowego. Ponieważ Zacznijmy więc od początku. Konstrukcja typename..
.
jednak w tych wielokrotnych argumentach pozostał już tylko jest tu najważniejsza, gdyż na tym właśnie zasadza się nowy
jeden argument, to ta postać zostanie również przekierowa- typ funkcji o zmiennej liście argumentów w C++. Konieczność
na do jednoargumentowej print. Jeśli argumentów jest więcej opierania się ich na wzorcach jest zresztą oczywista: sko-
wersja wieloargumentowa będzie wywoływana dotąd, aż li- ro nie wiemy, jakie argumenty zostaną przekazane do funk-
sta argumentów stopnieje do jednego. cji zwłaszcza, że nie wiemy, ile ich będzie to musimy zało-
Doug Gregor, w ramach żartu, wymyślił jeszcze prostszą żyć, że ich typ jest nieznany i powinien zostać wydedukowany,
postać (aczkolwiek ta właśnie wersja została wpisana do do- a do tego wzorce właśnie służą.
kumentu z opracowaną propozycją dla komitetu standaryza- Zatem konstrukcja typename... Args oznacza, że w rzeczy-
cyjnego C++) patrz Listing 2. Tutaj eat dostanie kolejne ar- wistości będzie się to rozwijało jako typename Arg1, typename
gumenty, z którymi nie robi nic, a cała akcja zostanie wykona- Arg2, ... i tak dalej. Oczywiście cyfr 1 i 2 użyłem tu symbolicz-
na przez wyrażenie z std::cout. nie, żeby można było łatwiej to sobie wyobrazić. Wspomniane
Args będzie potem używane już w definicji wzorca. Args jest tu-
Co to jest typename ... ? taj czymś w rodzaju wielokrotnego parametru wzorca i będzie
Dobrze, wiem przykład prosty, ale zrozumiale to on nie wy- nam odpowiednio definiował również wielokrotny argument
gląda. Fakt reguły, jakimi się rządzą variadic templates nie są funkcji (Doug określa to terminem argument pack).
specjalnie intuicyjne, ale dobre objaśnienie koncepcji variadic
A potem było Args... args , tak?
No właśnie. Pomińmy na razie postać, jaką pokazałem w przy-
kładzie przyjmijmy na razie taką postać jak tu w tytule rozdzia-
łu, bo tak będzie wygodniej. Taka konstrukcja definiuje nam tzw.
argument wielokrotny, co oznacza, że tego '
args' będzie można
następnie używać tak, jakby był po prostu zwykłym obiektem ty-
Deklaracja Wywołanie
pu 'Args' Ponieważ jest on jednak obiektem wielokrotnym, więc
.
jak się można domyślać trzeba teraz określić, co mamy zro-
bić z tym obiektem, a co z jego tzw. ogonem , który jest ozna-
czany jako .... To nie jest prosta sprawa i dla mnie również z po-
czątku nie wyglądało to intuicyjnie. Jednak po dyskusji z Do-
ugiem Gregorem doszedłem do wniosku, że faktycznie takie
Konkretyzacja
rozwiązanie jest najlepsze. Popatrzmy zatem na nasz pierwszy
przykład, a konkretnie linijkę, która używa 'args...':
print( args... );
Odnosząc się do wspomnianego "template... Args" powyż-
,
sza konstrukcja rozwinie się jako:
Deklaracja Użycie
print( arg1, arg2, arg3 ... itd );
Zaraz!
A skąd ten przecinek? I dlaczego tu?
Ano właśnie. Tu się mieści sedno sprawy. W istocie jest to
Konkretyzacja
znacznie bardziej prostackie, niż się wydaje: rzeczywiście,
Rysunek 1. Schemat Variadic templates rozwijanie wielokropka polega na zastąpieniu go listą argu-
Software Developer s Journal 05/2007 www.sdjournal.org
49
Programowanie
C++
chcemy mieć tak:
Listing 2. Prostsza postać funkcji print
template print( reformat( arg1 ), reformat( arg2 ), reformat( arg3 ) );
inline void eat(const Args&...) {}
No to przecież bardzo proste:
template
void print(const Args&... args) { print( reformat( args )... );
eat(std::cout << args...);
} Tu proszę zauważyć jedną bardzo istotną rzecz: wszystkie argu-
menty z listy wielokrotnego argumentu muszą zostać przez wy-
rażenie, które go zawiera (!), potraktowane w dokładnie ten sam
mentów , to znaczy właśnie, na wstawieniu m.in. odpowied- sposób. Nie możemy bezpośrednio w takim wywołaniu np. użyć
niej liczby przecinków. Drugie pytanie jednak okazuje się innego sposobu traktowania argumentów parzystych i nieparzy-
nieco trudniejsze, choć odpowiedz jest bardzo prosta: prze- stych. To też można oczywiście zrobić, zatrudniając do tego do-
cinek zostanie wstawiony tam, gdzie oryginalnie był wielo- datkową funkcję, która przyjmie co najmniej dwa argumenty (tyl-
kropek. To gdzie jest w takim razie początek wyrażenia, któ- ko trzeba dostarczyć wersję jednoargumentową, żeby zgłosić
re zostanie powtórzone? Otóż dokładnie tam, gdzie się za- błąd wywołania). No dobrze zgodnie z powyższymi objaśnie-
czyna wyrażenie zakończone wielokropkiem (podpowiedz: niami, można teraz jasno powiedzieć, co dokładnie oznacza ar-
początek wyrażenia nie może znajdować się przed otwar- gument const Args&... args . Będzie rozwinięte na coś w ro-
ciem nawiasu, który nie jest zamknięty przed końcem wyra- dzaju const Arg1& arg1, const Arg2& arg2 ... itd.
żenia). A bardziej na żywca? No to wyobrazmy sobie, że ten
argument przed przekazaniem do print() musi zostać jesz- To ja poproszę coś mocniejszego
cze przesiany przez funkcję reformat(). Czyli zamiast cze- Oczywiście variadic templates nie służą tylko do definiowa-
goś takiego: nia funkcji o zmiennej liście argumentów. Ponieważ jest to me-
chanizm dotyczący przede wszystkim wzorców, więc na po-
print( arg1, arg2, arg3 ); dobnej zasadzie można tworzyć wzorzec klasy. Stosuje się
Listing 3. Fragment kodu napisany wedle reguł współczesnego C++
// Tu przy mniejszej liczbie niż 6 reszta jest dopełniana EventDispatcher( handler_t hnd ): handler( hnd ) {}
"void" virtual bool Dispatch( const BasicEvent* basic_args )
// a zamiast function6 jest użyte odpowiednio function5 itd. const
template class Type4, class Type5, class Type6> const EventArgs* args = static_cast(
struct HandlerType basic_args );
{ return DispatchEventInternal( args, handler, Type1(),
typedef boost::function6 Type2, Type3, Type4, Type5, Type6> type; }
}; };
class BasicEventDispatcher // Tu wzorzec ma tyle parametrów, ile argumentów funkcji
{ "handler",
public: template virtual bool Dispatch( const BasicEvent* args ) const class Type1, class Type2, class Type3,
= 0; class Type4, class Type5, class Type6> inline
virtual ~BasicEventDispatcher() {} // a w argumentach DispatchEventInternal przy mniejszej ich
}; liczbie
template class Type1, class Type2, class Type3, bool DispatchEventInternal( const EventArgs* args,
class Type4, class Type5, class Type6> Function handler, Type1, Type2,
class EventDispatcher: public BasicEventDispatcher Type3, Type4, Type5, Type6 )
{ {
public: return handler( Shell( Type1(), *args ),
typedef typename HandlerType typename Type2::type, Shell( Type3(), *args ),
typename Type3::type, Shell( Type4(), *args ),
typename Type4::type, Shell( Type5(), *args ),
typename Type5::type, Shell( Type6(), *args ) );
typename Type6::type>::type handler_t; }
handler_t handler;
50
www.sdjournal.org
Software Developer s Journal 05/2007
Programowanie
C++
to, co prawda, również do tego, żeby móc wewnątrz klasy za- jest int to oznacza, że funkcja clicked _ button musi mieć
wrzeć mechanizmy pozwalające na obsługiwanie, za pomocą sygnaturę:
jednej spójnej definicji, funkcji o dowolnej liczbie argumentów.
Przejdzmy zatem do bardziej wypasionych przykładów. bool clicked_button( int );
Kod, którego tu użyję, powstał na użytek pewnej bibliote-
ki, którą z trudem usiłuję stworzyć od pewnego czasu, a ze Skojarzenie z Tcl/Tk i komendą [bind] jest jak najbardziej za-
względu na stopień komplikacji przy obsłudze funkcji o do- mierzone.
wolnej liczbie argumentów dobrze się nadawał zarówno do Procedura dispatchowania zdarzenia wygląda tak, że
zaprezentowania możliwości variadic templates, jak i do do- oryginalnie zdarzenie przesyła wszystkie możliwe dane
kładnego przetestowania łatki Douglasa Gregora do kompila- w odpowiedniej strukturze, takiej jak np. wspomniana But-
tora gcc, która dodaje ową właściwość języka. tonPressEvent (patrz argument typu EventArgs w przytoczo-
Na Listingu 3 przedstawiony został kod, który napisałem nym kodzie). Następnie, w zależności od podanych znacz-
pierwotnie, wedle obecnych reguł C++. Proszę zwrócić uwa- ników, z tej struktury funkcja Shell wyłuskuje odpowiednie
gę na definicje pośredniczące, które musiałem stworzyć, że- wartości, podawane następnie do handlera (patrz Dispat-
by wyodrębnić fragmenty odpowiedzialne za obsługę funkcji chEventInternal).
z odpowiednią liczbą argumentów. Tu argumentów jest mak- Na Listingu 4 zaś ten sam fragment, zapisany w konwencji
symalnie 6, co uznałem za dobry kompromis pomiędzy przy- variadic templates.
datnością dla użytkownika, a wrodzonym lenistwem. Z tych Porównując te Listingi, proszę zwrócić uwagę na koniecz-
zwielokrotnionych definicji podam oczywiście nie wszystkie, ne pośredniki w pierwszej wersji. Przykładowo HandlerType
a jedynie te z maksymalną liczbą parametrów w komentarzu jest konieczny, żeby można było mapować listę argumentów
podałem, jak są konstruowane pozostałe, a przytaczanie ich (włącznie z nieużywanymi) na odpowiedni typ boost::function
tu byłoby niepotrzebnym marnowaniem miejsca. (np. żeby HandlerType zo-
Może najpierw jeszcze parę słów o przeznaczeniu tego stał zamieniony na function1). Mając to, można
kodu. Jest to fragment biblioteki odpowiedzialnej za rejestro- użyć HandlerType z pełną listą typów parametrów. W przeciw-
wanie i dispatchowanie zdarzeń. W zamierzeniu ma służyć ja- nym razie musiałaby być powielona cała definicja wzorca kla-
ko pośrednik do rozsyłania zdarzeń GUI. Funkcjonalność re-
jestrowania zdarzeń nie jest tu zamieszczona (aż tyle nie po-
Listing 4. Fragment kodu zapisany w konwencji variadic
trzeba na użytek przykładu). Podpowiem tylko, że rejestrowa-
templates
nie (funkcja bind _ event) tworzy nowy obiekt typu specjalizo-
wanego z EventDispatcher, a ten obiekt służy z kolei jako po- class BasicEventDispatcher
średnik w wywołaniu handlera zdarzenia. Jest to tak złożone, {
z tego względu, że funkcja rejestrująca ma mieć możliwość public:
podania jej funkcji o dowolnej liczbie argumentów (wiem, po- virtual bool Dispatch( const BasicEvent* args ) = 0;
wtarzam się), plus specjalnych znaczników określających, ja- virtual ~BasicEventDispatcher() {}
kie dane, przychodzące z tego zdarzenia, mają być przekazy- };
wane do funkcji na przykład tak: template
class EventDispatcher: public BasicEventDispatcher
bind_event( w, Right-Control-Button-1, &clicked_button, ea_ {
windid ); public:
typedef function
co oznacza, że rozsyłacz zdarzeń w ma w przypadku zda- handler_t;
rzenia określonego jako Right-Control-Button-1 (zgadłeś, czy- handler_t handler;
telniku, to jest konstruowane przeciążonym operatorem - ) EventDispatcher( handler_t hnd ): handler( hnd ) {}
wywołać funkcję clicked _ button, podając jej jako argument virtual bool Dispatch( const BasicEvent* basic_args )
to, co jest opisane znacznikiem ea _ winid. Mówiąc najbardziej {
na skróty, zdarzeniu Button odpowiada struktura argumentów const EventArgs* args = static_cast(
ButtonPressEvent, pochodna BasicEvent, i ta ostatnia zawiera basic_args );
pole o nazwie winid. Przypisanie ea _ winid do tego pola pole- return DispatchEventInternal( args, handler,
ga na stworzeniu następującej funkcji: Types()... );
}
ea_winid_t::type Shell( ea_winid_t, const BasicEvent& args ) };
{ return args.winid; }
template Każdy znacznik ma swój unikalny typ, aby można było wy- typename... Types>
korzystać przeciążanie, i w definicji tego typu zawiera się bool DispatchEventInternal( const EventArgs* args, Function
odpowiedni typedef, który określa typ danej obsługiwanej handler, Types... types )
przez znacznik. Ilość znaczników podawanych do bind _ {
event ma być w założeniu dowolna, a sygnatura funkcji han- return handler( Shell( types, *args )... );
dlera jest ściśle związana z ilością znaczników i przypisa- }
nymi im typami. Załóżmy, że typem przypisanym ea _ winid
52
www.sdjournal.org
Software Developer s Journal 05/2007
Variadic templates w C++0x
Zastosowania
Autorzy koncepcji variadic templates nie poprzestali na samej
W Sieci
propozycji wzbogacenia języka C++ o określoną właściwość, ale
" http://www.open-std.org/jtc1/sc22/wg21/ strona domowa ko-
zaproponowali również nową wersję odpowiednich elementów
mitetu standaryzacyjnego języka C++,
biblioteki standardowej, w tym również elementów, które aktual-
" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/
nie są proponowane do TR1. Jedną z najważniejszych jest tuple,
n2142.html wykaz aktualnych zagadnień opracowywanych
znana już dziś jako boost::tuple ( generalizacja std::pair ) i za-
dla C++0x,
proponowana do TR1. Przystosowanie do tego ficzera tr1::func-
" http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.html
tion i tr1::bind jest już i tak oczywiste. Do tr1::bind jednak przy-
strona domowa ficzera,
dałby się moim zdaniem jeszcze jeden dodatkowy ficzer tak,
" http://www.boost.org/ biblioteki boost, w tym tuple, function,
jak ma np. _ 1, _ 2, żeby miał jeszcze coś w rodzaju _ N, oznacza-
bind i signal.
jący, że ma przekazać oryginalnej funkcji wszystkie argumen-
ty, które dostanie binder przy wywołaniu. W propozycji znajdują
sy EventDispatcher, bo dla każdej liczby argumentów należa- się również elementy związane z koncepcjami. Co ciekawe, jest
łoby użyć innego typu boost::function. możliwa również konstrukcja:
Kolejnym pośrednikiem jest DispatchEventInternal. Nie
usunąłem go z podanego przykładu, ale nie dlatego, że- template
by to było niemożliwe (pozostawiam to jako ćwiczenie dla
czytelnika), tylko dlatego, że dzięki temu przykład lepiej chociaż tylko w przypadku funkcji. Ale to wystarczy, żeby np.
ilustruje sposoby konstruowania wyrażeń z użyciem argu- wznowić dyskusję na temat ścisłych wyjątków w C++. Mając
ment pack. to rozszerzenie, oraz możliwość opcjonalnego specyfikowa-
Proszę tutaj z tego właśnie względu zwrócić szczególną nia określonych właściwości języka (składnia do tego została
uwagę na pozycję, w której zapisano wielokropek i wyobra- zaproponowana przy Garbage Collection ) można by się po-
zić sobie, co dokładnie zostanie powielone. Jak widać, powie- kusić o opcjonalne ścisłe wyjątki w C++ bez stwarzania pro-
leniu ulega całość wyrażenia od jego początku, a wielokropek blemów z funkcjami typu std::find_if. Ścisłe, to znaczy takie,
umieszczamy na jego końcu tam, gdzie powinien zostać po- w których niepowodzenie zgłaszane przez std::unexpected
stawiony pierwszy przecinek. byłoby wykrywane już podczas kompilacji.
Jako kolejne ćwiczenie proponuję napisać funkcję, która
będzie przyjmowała coś w rodzaju argumentów ze znaczni- Podsumowanie
kami . To znaczy, będzie przyjmowała zawsze parzystą liczbę Ficzer variadic templates na pewno pomoże rozwiązać wiele pro-
argumentów, gdzie pierwszy z pary będzie symboliczną na- blemów, z którymi borykają się wszelkie nowoczesne bibliote-
zwą znacznika, a drugi argumentem funkcji odpowiadającym ki używające zaawansowanych właściwości C++. W tym arty-
temu znacznikowi. Przykładowe wywołanie: kule przedstawiłem, mam nadzieję, wszystkie sprawy związane
z variadic templates i ich konsekwencją dla języka C++ oraz je-
fn( FN_COLOR, Red, FN_HEIGHT, 20 ); go bibliotek. Jest to o tyle ważna właściwość, że ma dużą szan-
sę przyczynić się do zmniejszenia niepotrzebnego, nadmiarowe-
Istniejące dodatki składniowe go kodu i uczynić przez to kod łatwiejszym do zarządzania. Nie
Z ostatniej wersji propozycji variadic templates, następujące obyło się oczywiście bez odpowiedniego skomplikowania języka
składnie związane z tym ficzerem mają być dodane do C++0x ale ja osobiście zawsze twierdziłem, że zwiększanie możliwo-
poza rozwijaniem wyrażenia w argumenty funkcji: ści języka musi iść w parze ze zwiększaniem jego komplikacji.
Ten ficzer oraz projekt koncepcji, choć są wciąż w fazie
" pobieranie długości listy wielokrotnych argumentów tu opracowywania propozycji zmiany do standardu, doczekały
używa się składni sizeof...(Args). Poprzednia składnia si- się już publicznie dostępnych próbnych implementacji (łatę na
zeof (Args...) była cokolwiek niejednoznaczna variadic templates do gcc 4.1.1 można ściągnąć ze strony do-
" rozwijanie listy inicjalizacyjnej, na przykładzie boost::array mowej ficzera patrz ramka W Sieci). Jest to nowość w do-
template tychczasowym procesie standaryzacji C++ i wnosi pewien po-
void f( Types... args ) wiew optymizmu. Byłoby bardzo miło, gdyby i inne propozy-
{ cje do standardu doczekały się takich właśnie próbnych im-
boost::array = { args... }; plementacji moim zdaniem następne w kolejce jest auto/
} decltype. Niewykluczone też, że przy uzyskaniu odpowiedniej
" Rozwijanie klas bazowych w dziedziczeniu (i przy okazji stabilności zostaną one dodane również do oficjalnych dystry-
inicjalizacja każdej z nich w konstruktorze): bucji gcc, oczywiście jako eksperymentalne ficzery C++. Pyta-
template łem zresztą Douga o to i powiedział, że na razie są problemy
class SomeClass: public Bases... z licencją stworzonego przez niego kodu (właścicielem jest
{ wciąż Indiana University) ale ich prawnicy pracują nad tym.
SomeClass( const Bases&... bases ): Bases( bases )... {} Oczywiście, ten ficzer nie jest jeszcze gotowy i wiele mo-
}; że się w nim zmienić. Zachęcam jednak do zaopatrzenia się
" Rozwijanie specyfikacji wyjątków w wersję gcc z tym dodatkiem (w celach edukacyjnych) oraz
template przesyłania ewentualnych uwag Douglasowi. Może i Ty przy-
void fn() throw( Exceptions... ) {} czynisz się do ulepszenia standardu C++... n
Software Developer s Journal 05/2007 www.sdjournal.org
53
Wyszukiwarka
Podobne podstrony:
2007 05 Type Tool Texmacs a Convenient Layout Program for Your Text Documents
2007 05 P
2007 05 Filtering Vista
05 Praktyczne wskazówki do programu Access
2006 05 Lazarus i pudełko z hasłami [Programowanie]
2007 05 in a Flash Cross Browser Internet Applications with Openlaszlo
2007 03 Pomysły na szybkie C [Programowanie]
2007 05 Mechanizm koncepcji w języku C nowe oblicze szablonów [Inzynieria Oprogramowania]
2007 05 Szkoła konstruktorówid 654
2007 05 02
2007 05 R
2007 12?tekcja i rozpoznanie twarzy w C [Programowanie C C ]
więcej podobnych podstron