Elementy usunięte w C++17
• tech • 1214 słów • 6 minut czytania
W przyszłym roku prawdopodobnie wejdzie w życie nowa, większa odsłona (tzw. major release) standardu języka C++, czyli C++17. Od dłuższego czasu, odkąd C++14 ujrzało światło dzienne, zacząłem więcej zajmować się reverse engineeringiem (w pracy) oraz (w domu) powróciłem do starego hobby, jakim jest elektronika. Przez co moje programowanie stało się mocno niszowe i sporadyczne, co pociągło za sobą mniejsze zainteresowaniem się tym co dzieje się wokół języka C++ i jego społeczności. Co nie znaczy, że nie czekam na C++17, wręcz przeciwnie!
Core
Kilka rzeczy dotąd dostępnych w języku wraz z wchodzącym C++11 zostały oznaczone jako deprecated
i teraz w C++17 zostaną usunięte.
register
W C++17 usunięto specyfikator register
(N4340), co mnie mocno zaciekawiło. Nawet nie wiedziałem że zostało ono deprecated 5 lat temu. Modyfikator ten był podpowiedzią dla kompilatora (kiedyś może nawet wymuszał lub nakazywał?), że dana zmienna będzie często używana i warto przechowywać ją w jednym z rejestrów (procesora). Oczywiście jak to bywa na przykład z inline
, to jest tylko podpowiedź i kompilator może ją po prostu olać zignorować.
A
register
specifier is a hint to the implementation that the variable so declared will be heavily used. [ Note: The hint can be ignored and in most implementations it will be ignored if the address of the variable is taken. This use is deprecated (see D.2). —end note ]
Tak też bywa w nowoczesnych kompilatorach, które same na podstawie swoich algorytmów i przewidywań potrafią lepiej zorganizować przechowywanie kodu i całość bardzo dobrze zoptymalizować. Sugestie takie straciły swoją moc, dlatego zdecydowano się je usunąć z języka. Dlatego w C++17 to słowo kluczowe jest nieużywane, ale pozostaje zarezerwowane. Zapewne dla kompatybilności, być może nawet z podsystemem języka C, bo tam też od C99 taki specyfikator występuje i robi podobne rzeczy co w młodszym bracie.
O ile zgadzam się z tym, że obecne kompilatory dla najbardziej rozbudowanych i popularnych platform oraz architektur jak x86/x64/ARM potrafią odwalić kawał dobrej roboty. To nie sądziłem, że w innych, szczególnie tych mniejszych (emebeded), 8/16-bitowcach również dają radę. Myślałem, że w takich na przykład AVR-ach czy ‘51, dobrze czasem mieć większą kontrole nad generowanym kodem. Ale jak popatrzymy na to z punktu widzenia abstrakcji języka i kompilatora, to tutaj również dobry kompilator wykona wyśmienitą robotę i ten pogląd również traci swój sens. Chociaż taka możliwość może się przydać jeśli przeplatamy kod ze wstawkami asemblera. Ale to i tak zawsze będzie vendor specific i zależne od danej platformy, czy rozszerzeń, bądź implementacji kompilatora, vide rozszerzenie w gcc (Explicit Register Variables), czy gcc-avr (How to permanently bind a variable to a register?).
To chyba zamyka dla mnie już definitywnie problem specyfikatora register
.
Za to mam inną prośbę. Czemu do tej pory nie ustandaryzowano słówka kluczowego asm
? Takiego po części połączenia implementacji z GCC i VS, żeby można było podawać zmienne i parametry podobnie jak w GCC, ale bez tego całego babrania się w stringi i nawiasy, za to z klamerkami jak w VC… Może sam powinienem taką propozycję do komitetu standaryzującego przygotować. Mechanizm ten mógłby się bardzo przydać właśnie w małych embedowych projektach…
bool++
Mówiąc szczerze o tej możliwości języka nie miałem bladego pojęcia. Zgodnie ze standardem, operatory pre- i post- inkrementacji mogą być używane do operandów typu boolowskiego, ustawiając ich wartość na true
. Poniższy kod jest jak najbardziej poprawny:
bool x = false; // x = false
x++; // x = true
++x; // x = true
Chociaż zachowuje się dość nietypowo w porównaniu do typów całkowito-liczbowych. Inkrementacja działa tylko do ustawiania wartości true
. Ciekawy jestem jaki był cel wprowadzenia takiego mechanizmu w C++. W obecnie przygotowywanym C++17 możliwość ta zostaje usunieta, po wcześniejszym deprecated w C++11.
Trojznaki
W C++ podobnie jak w C i innych językach istnieją dwu- (digraphs) i trzy- (trigraphs) znakowce będące alternatywą do specyficznych znaków specjalnych używanych w składni języka, a w różnych systemach będących mapowanymi na znaki narodowe lub w ogóle niedostępne.
Poniższy, standardowy kod w czystym formacie ASCII:
#define VAL 0
int main() {
int x[] = { ... }
if (x[1] != VAL) {
printf("x=%d\n", x[0] ^ x[1]);
}
return 0;
}
można zapisać w alternatywny sposób z użyciem trójek znaków:
??=define VAL 0
int main() ??<
int x??(??) = ??< ... ??>
if (x??(1??) != VAL) ??<
printf("x=%d??/n", x??(0??) ??' x??(1??));
??>
return 0;
??>
oraz z użyciem słów kluczowych i par znaków zdefiniowanych dodatkowo w C++:
%:define VAL 0
int main() <%
int x<::> = <% ... %>
if (x<:1:> not_eq VAL) <%
printf("x=%d??/n", x<:0:> xor x<:1:>);
%>
return 0;
%>
Mechanizm ten został niemal od początku powstania standardu przyjęty przez komitet. Sam B. Stroustrup w jednej ze swoich książek (Projektowanie i rozwój języka C++) wspomina o tym tak:
Przypuszczam, że w okresie przejściowym, być może trwającym około jednego dziesięciolecia, słowa zastrzeżone, pary i trójki znaków są najmniejszym złem. Mam nadzieję, że to pomoże językowi C++ przyjąć się tam, gdzie nie udało się przebić językowi C…
Kilka dziesięcioleci upłynęło, aż ostatnio przy pracach nad C++11 próbowano pozbyć się tych historycznych naleciałości (N2910) - wszakże chyba mało kto używa innych lub narodowych zestawów znaków w swoim środowisku deweloperskim - ale przedstawiciele IBM-a byli innego zdania i formalnie się nie udało. Obecnie przy C++17 ponawia się tą prośbę (N3981, N4086), ale z bezpośrednim usunięciem, bez dodatkowego czasu na zabawę w deprecated
. Dyskusje z IBM-em w tym temacie już się rozgrzały (N4210) ;)
STL
Czystki również nie ominą standardowej biblioteki. Kilka nowych elementów i mechanizmów w C++11 zastąpiło stare elementy, które zostały oznaczone jako deprecated
lub stały się archaiczne i wraz z C++17 znikną na zawsze z STL-a. Nie będę tutaj za bardzo wchodził w szczegóły, dokument N4190 dokładnie opisuje sytuację, został on zaakceptowany już na pierwszym meetingu C++17 (3-8 listopad 2014, Urbana-Champaign, IL, USA).
Mowa tutaj o nieszczęsnym auto_ptr
, które od dawna było małą kulą u nogi, czy ktoś kiedyś w ogóle z niego korzystał po C++03, a tym bardziej C++11?
Gdy boost wprowadził do swojej oferty lambda
i bind
, mało kto zaprzątał sobie głowę standardowymi na ówczesne czasy binderami - bind1st
i bind2nd
. A po wchłonięciu boostowych mechanizmów do standardu w C++11 stare elementy odeszły w niepamięć. W przyszłości bind również zniknie, bo lambda zajęła jego miejsce ;)
Wraz z starymi binderami, podobny los dotyka archaiczne funktory i adaptery: unary_function
/binary_function
, ptr_fun
, mem_fun
/mem_fun_ref
.
Znika również random_shuffle
ze standardowej biblioteki.
Coś jeszcze?…
Większość usuniętych elementów z jadra języka czy biblioteki standardowej rzeczywiście jest przestarzała, a wręcz archaiczna jak na obecnie panujące czasy. Nieźle bym się zdziwił, jeśli spotkałbym te elementy używane w nowoczesnym kodzie, choć może ktoś jeszcze wspiera takie rzeczy w starym?
Jason Turner w jednym ze swoich odcinków C++ Weekly fajnie podsumował usuwane elementy z języka - Ep 26 Language Features Removed in C++17:
www.youtube.com/watch?v=uCRkBXQhud0
Warto się zapoznać z tym materiałem, jak również z dokumentami i propozycjami jakie trafiły bądź zostały opracowane przez komitet standaryzacyjny, dotyczącymi tych mechanizmów i elementów. Starłem się dołączać odnośniki do nich w tekście.
Może znajdę więcej czasu i przyjrzę się bliżej tym co wnosi C++17. Poprzednie zmiany, choć było ich mało w ‘14, to za bardzo się nimi nie interesowałem. Teraz będzie ich znacznie więcej, podobnie jak to było z C++11. Uważam, że warto co jakiś czas popatrzeć w kierunku wprowadzanych zmian. Nowe elementy języka pociągają za sobą nowe możliwości, łatwiejszą pracę, no i nowe idiomy, które zapewne warto znać, jeśli chce się podążać wraz z duchem modern C++…
Komentarze (1)
register
w najstarszych (początek lat 90-tych) książkach, jakie pamiętam było opisywane jako sugestia. Do rozwiązań typu local/vendor specific można zawsze użyć np.pragma
.