Elementy usunięte w C++17

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:

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++…

Jedno przemyślenie nt. „Elementy usunięte w C++17”

  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.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *