Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00039 004955 18973052 na godz. na dobę w sumie
C++17 STL. Receptury - książka
C++17 STL. Receptury - książka
Autor: Liczba stron: 456
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-4501-0 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c++ - programowanie
Porównaj ceny (książka, ebook (-35%), audiobook).

C++ pozwala zarówno na tworzenie interfejsów wysokiego poziomu, jak i na pisanie kodu działającego na niskim poziomie. Sprawdza się, gdy trzeba zapewnić wysoką wydajność i niskie obciążenie. Język ten jest konsekwentnie rozwijany: kolejne jego specyfikacje, C++14 i C++17, przyniosły wiele znakomitych udoskonaleń. Aby w pełni wykorzystać ten potencjał, należy korzystać z C++ łącznie z biblioteką STL. Jest to standardowa biblioteka języka, dzięki której C++ jest idealny do implementowania oprogramowania o wysokiej jakości i dużej wydajności. Zalety C++ sprawiają, że jest wykorzystywany niemal w każdej dziedzinie. Niestety, wielu programistów nie używa STL.

Dzięki tej książce poznasz użyteczność biblioteki standardowej (STL) w C++17 w praktyce, co pozwoli Ci na tworzenie efektywniejszego i w pełni przenośnego kodu źródłowego. Najpierw poznasz nowe funkcje języka, co pozwoli Ci na zrozumienie reguł rządzących C++, oraz funkcje biblioteki standardowej i sposób jej działania. Podczas pracy nad praktycznymi i łatwymi do wykorzystania recepturami poznasz podstawowe koncepcje STL, takie jak kontener, algorytm, klasa narzędziowa, wyrażenie lambda, iterator i wiele innych. Dowiesz się, jak działają najnowsze funkcje wprowadzone w standardzie C++17. Dzięki temu zaoszczędzisz czas i wysiłek podczas programowania, a Twój kod stanie się prostszy i zdecydowanie bardziej elegancki.

W książce między innymi:

C++17 i STL. Elegancka klasyka i potężne możliwości.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: C++17 STL Cookbook Tłumaczenie: Robert Górczyński ISBN: 978-83-283-4501-0 Copyright © Packt Publishing 2017. First published in the English language under the title ‘C++17 STL Cookbook – (9781787120495)’ Polish edition copyright © 2018 by Helion SA All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/cpp17r Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:258)ci O autorze O redaktorze merytorycznym Wprowadzenie Rozdzia(cid:239) 1. Nowe funkcje w C++17 Wprowadzenie U(cid:285)ycie strukturalnych wi(cid:200)za(cid:241) do rozpakowania warto(cid:258)ci zwrotnej Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Ograniczanie zasi(cid:218)gu zmiennej do konstrukcji if i switch Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Zalety stosowania nowych regu(cid:239) inicjalizacji z u(cid:285)yciem sk(cid:239)adni opartej na nawiasach Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Umo(cid:285)liwienie konstruktorowi automatycznego okre(cid:258)lenia typu klasy szablonu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? U(cid:285)ycie wyra(cid:285)enia constexpr-if do uproszczenia decyzji podejmowanych podczas kompilacji Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? W(cid:239)(cid:200)czenie bibliotek w postaci samych nag(cid:239)ówków z u(cid:285)yciem osadzonych zmiennych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? 13 14 15 21 21 22 22 24 24 26 26 27 28 29 29 30 31 31 31 32 33 34 35 36 37 37 38 39 Poleć książkęKup książkę Spis tre(cid:286)ci Implementowanie za pomoc(cid:200) wyra(cid:285)e(cid:241) fold przydatnych funkcji pomocniczych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Rozdzia(cid:239) 2. Kontenery STL Wprowadzenie Magazyn danych znajduj(cid:200)cych si(cid:218) obok siebie Magazyn danych w postaci listy Drzewo wyszukiwania Tabela warto(cid:258)ci hash Adapter kontenera U(cid:285)ycie stylu usu(cid:241) – wyma(cid:285) w kontenerze std::vector Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Usuwanie w czasie O(1) elementów z nieposortowanego kontenera std::vector Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Uzyskanie bezpiecznego dost(cid:218)pu do egzemplarzy std::vector Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Sortowanie egzemplarzy std::vector Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Efektywne i warunkowe wstawianie elementów do kontenera std::map Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Stosowanie nowej semantyki podpowiedzi podczas wstawiania elementów za pomoc(cid:200) std::map::insert Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Efektywne modyfikowanie kluczy elementów std::map Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? U(cid:285)ycie kontenera std::unordered_map z niestandardowymi typami danych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Filtrowanie duplikatów w danych wej(cid:258)ciowych u(cid:285)ytkownika i wy(cid:258)wietlanie ich w kolejno(cid:258)ci alfabetycznej za pomoc(cid:200) kontenera std::set Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? 4 40 40 41 41 47 48 48 49 49 50 50 50 51 52 54 54 55 57 58 58 59 60 60 60 62 62 63 63 65 66 66 66 68 68 69 70 72 72 73 73 75 76 76 77 Poleć książkęKup książkę Spis tre(cid:286)ci Implementowanie za pomoc(cid:200) kontenera std::stack prostego kalkulatora RPN 79 80 82 84 Implementowanie za pomoc(cid:200) kontenera std::map licznika cz(cid:218)stotliwo(cid:258)ci wyst(cid:218)powania s(cid:239)ów 85 85 87 Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie za pomoc(cid:200) kontenera std::set narz(cid:218)dzia pomocniczego przeznaczonego do wyszukiwania bardzo d(cid:239)ugich zda(cid:241) w tek(cid:258)cie Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Implementowanie za pomoc(cid:200) kontenera std::priority_queue listy rzeczy do zrobienia Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Rozdzia(cid:239) 3. Iteratory Wprowadzenie Kategorie iteratorów Tworzenie w(cid:239)asnego zakresu, który mo(cid:285)na iterowa(cid:202) Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Tworzenie w(cid:239)asnych iteratorów zgodnych z kategoriami iteratora STL Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? U(cid:285)ycie adapterów iteratora do wype(cid:239)niania ogólnych struktur danych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie algorytmów w kategoriach iteratorów Jak to zrobi(cid:202)? Co dalej? Iteracja w drug(cid:200) stron(cid:218) za pomoc(cid:200) adaptera iteratora odwrotnego Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Zako(cid:241)czenie dzia(cid:239)ania iteratora w zakresie za pomoc(cid:200) wartownika iteratora Jak to zrobi(cid:202)? Automatyczne sprawdzanie kodu iteratora Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Tworzenie w(cid:239)asnego adaptera iteratora (cid:239)(cid:200)czenia na zak(cid:239)adk(cid:218) Jak to zrobi(cid:202)? Co dalej? 88 89 91 92 93 93 95 97 97 99 101 101 103 104 104 106 107 107 107 109 110 111 113 114 114 115 116 117 119 119 122 123 123 125 128 5 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 4. Wyra(cid:285)enia lambda Wprowadzenie Definiowanie funkcji opartej na wyra(cid:285)eniu lambda Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Dodawanie polimorfizmu poprzez opakowanie wyra(cid:285)enia lambda egzemplarzem std::function Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? (cid:146)(cid:200)czenie funkcji za pomoc(cid:200) konkatenacji Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Tworzenie skomplikowanych predykatów z logiczn(cid:200) koniunkcj(cid:200) Jak to zrobi(cid:202)? Co dalej? Wywo(cid:239)ywanie wielu funkcji dla tych samych danych wej(cid:258)ciowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie funkcji transform_if() za pomoc(cid:200) algorytmu std::accumulate i wyra(cid:285)e(cid:241) lambda Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Generowanie w trakcie kompilacji iloczynu kartezja(cid:241)skiego par dla dowolnych danych wej(cid:258)ciowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Rozdzia(cid:239) 5. Podstawy algorytmów biblioteki STL Wprowadzenie Kopiowanie elementów mi(cid:218)dzy kontenerami Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Sortowanie kontenera Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Usuwanie okre(cid:258)lonych elementów z kontenera Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Przekszta(cid:239)canie zawarto(cid:258)ci kontenera Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Wyszukiwanie elementów w uporz(cid:200)dkowanych i nieuporz(cid:200)dkowanych wektorach Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Ograniczanie za pomoc(cid:200) std::clamp warto(cid:258)ci wektora do okre(cid:258)lonego zakresu liczbowego Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? 6 131 131 133 133 136 138 138 140 141 142 143 144 145 146 146 147 148 150 150 152 155 156 158 161 161 163 164 166 167 167 170 171 171 173 174 174 176 176 177 180 182 182 185 Poleć książkęKup książkę Spis tre(cid:286)ci Wyszukiwanie za pomoc(cid:200) std::search wzorca w ci(cid:200)gu tekstowym i wybór optymalnej implementacji Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Próbkowanie ogromnego wektora Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Generowanie permutacji sekwencji danych wej(cid:258)ciowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie narz(cid:218)dzia (cid:239)(cid:200)czenia s(cid:239)owników Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Rozdzia(cid:239) 6. Zaawansowane przyk(cid:239)ady u(cid:285)ycia algorytmów biblioteki STL Wprowadzenie Implementowanie klasy drzewa trie za pomoc(cid:200) algorytmów STL Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie za pomoc(cid:200) drzewa trie generatora sugestii danych wej(cid:258)ciowych u(cid:285)ywanych podczas wyszukiwania Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Implementowanie wzoru przekszta(cid:239)cenia Fouriera za pomoc(cid:200) algorytmów STL Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Obliczanie b(cid:239)(cid:218)du sumy dwóch wektorów Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementowanie procedury generuj(cid:200)cej dane ASCII dla zbioru Mandelbrota Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Opracowanie w(cid:239)asnego algorytmu — podzia(cid:239) danych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Po(cid:239)(cid:200)czenie u(cid:285)ytecznych algorytmów biblioteki STL — zbieranie danych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Usuwanie nadmiarowych bia(cid:239)ych znaków znajduj(cid:200)cych si(cid:218) mi(cid:218)dzy s(cid:239)owami Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Kompresja i dekompresja ci(cid:200)gów tekstowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? 186 186 188 189 190 192 193 193 194 195 195 197 199 200 201 201 204 206 206 210 210 211 212 217 218 218 220 221 223 226 227 228 229 230 231 231 233 235 235 237 238 238 240 241 7 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 7. Ci(cid:200)gi tekstowe, klasy strumieni i wyra(cid:285)enia regularne Wprowadzenie Tworzenie, konkatenacja i przekszta(cid:239)canie ci(cid:200)gów tekstowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Usuwanie bia(cid:239)ych znaków z pocz(cid:200)tku i ko(cid:241)ca ci(cid:200)gu tekstowego Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Komfortowe u(cid:285)ycie klasy std::string bez kosztów zwi(cid:200)zanych z tworzeniem obiektów std::string Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Odczyt warto(cid:258)ci z danych wej(cid:258)ciowych dostarczonych przez u(cid:285)ytkownika Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Zliczanie wszystkich s(cid:239)ów w pliku Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Formatowanie danych wyj(cid:258)ciowych za pomoc(cid:200) manipulatorów strumienia wej(cid:258)cia – wyj(cid:258)cia Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Inicjalizacja skomplikowanych obiektów na podstawie pliku (cid:283)ród(cid:239)owego Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Wype(cid:239)nianie kontenera za pomoc(cid:200) iteratorów std::istream Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Proste wy(cid:258)wietlanie danych za pomoc(cid:200) iteratorów std::ostream Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Przekierowywanie sekcji kodu do pliku danych wyj(cid:258)ciowych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Tworzenie w(cid:239)asnych klas ci(cid:200)gu tekstowego za pomoc(cid:200) dziedziczenia po klasie std::char_traits Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Tokenizowanie danych wej(cid:258)ciowych za pomoc(cid:200) biblioteki wyra(cid:285)e(cid:241) regularnych Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Wygodne formatowanie liczb w locie w zale(cid:285)no(cid:258)ci od kontekstu Jak to zrobi(cid:202)? Przechwytywanie na podstawie b(cid:239)(cid:218)dów std::iostream wyj(cid:200)tków mo(cid:285)liwych do odczytania Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? 8 243 244 245 246 248 248 249 250 251 252 254 254 255 257 258 258 260 260 261 264 266 266 268 269 269 272 273 273 276 277 278 280 281 282 286 287 287 289 291 291 293 294 296 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 8. Klasy narz(cid:218)dziowe Wprowadzenie Konwertowanie mi(cid:218)dzy ró(cid:285)nymi jednostkami czasu za pomoc(cid:200) std::ratio Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Konwertowanie mi(cid:218)dzy bezwzgl(cid:218)dnymi i wzgl(cid:218)dnymi warto(cid:258)ciami czasu za pomoc(cid:200) std::chrono Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Bezpieczne sygnalizowanie awarii za pomoc(cid:200) typu std::optional Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? U(cid:285)ycie funkcji wraz z krotkami Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Szybkie opracowywanie struktur danych za pomoc(cid:200) std::tuple Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Zast(cid:200)pienie void* przez std::any dla zwi(cid:218)kszenia bezpiecze(cid:241)stwa typu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Przechowywanie ró(cid:285)nych typów za pomoc(cid:200) std::variant Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Automatyczna obs(cid:239)uga zasobów za pomoc(cid:200) std::unique_ptr Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Automatyczna obs(cid:239)uga wspó(cid:239)dzielonej pami(cid:218)ci na stercie za pomoc(cid:200) std::shared_ptr Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Praca ze s(cid:239)abymi wska(cid:283)nikami do wspó(cid:239)dzielonych obiektów Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Uproszczenie obs(cid:239)ugi zasobów przestarza(cid:239)ych API za pomoc(cid:200) sprytnych wska(cid:283)ników Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Wspó(cid:239)dzielenie ró(cid:285)nych warto(cid:258)ci sk(cid:239)adowych tego samego obiektu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Generowanie liczb losowych i wybór odpowiedniego silnika do generowania tego rodzaju liczb Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Generowanie liczb losowych i umo(cid:285)liwienie bibliotece STL okre(cid:258)lenia szczegó(cid:239)ów rozk(cid:239)adu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? 297 298 298 299 301 303 304 304 306 307 307 310 311 311 313 313 314 318 320 321 323 323 324 327 329 329 332 333 333 336 337 338 339 341 342 343 344 345 346 347 348 349 353 354 354 359 9 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 9. Programowanie równoleg(cid:239)e i wspó(cid:239)bie(cid:285)no(cid:258)(cid:202) Wprowadzenie Automatyczne stosowanie programowania równoleg(cid:239)ego w kodzie utworzonego za pomoc(cid:200) standardowych algorytmów Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? U(cid:258)pienie programu na podany okres czasu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Uruchamianie i zatrzymywanie w(cid:200)tków Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Przeprowadzanie bezpiecznego pod wzgl(cid:218)dem wyj(cid:200)tków nak(cid:239)adania blokady wspó(cid:239)dzielonej za pomoc(cid:200) std::unique_lock i std::shared_lock Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Zapobieganie zakleszczeniom dzi(cid:218)ki stosowaniu algorytmu std::scoped_lock Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Synchronizacja jednoczesnego u(cid:285)ycia algorytmu std::cout Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Bezpieczne odk(cid:239)adanie inicjalizacji za pomoc(cid:200) std::call_once Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Przesuni(cid:218)cie zadania do wykonywania w tle za pomoc(cid:200) std::async Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Implementacja wzorca producent – konsument za pomoc(cid:200) std::condition_variable Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementacja wzorca producent – konsument za pomoc(cid:200) std::condition_variable Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Równoleg(cid:239)e generowanie za pomoc(cid:200) std::async danych ASCII dla zbioru Mandelbrota Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Implementacja za pomoc(cid:200) std::future niewielkiej biblioteki automatycznej programowania równoleg(cid:239)ego Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? 361 362 363 363 365 369 369 370 371 371 373 375 375 378 381 382 384 384 385 386 388 389 390 390 391 393 395 395 396 398 400 400 404 406 406 409 410 411 415 10 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 10. System plików Wprowadzenie Implementowanie programu przeprowadzaj(cid:200)cego normalizacj(cid:218) (cid:258)cie(cid:285)ki dost(cid:218)pu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Pobieranie kanonicznej (cid:258)cie(cid:285)ki dost(cid:218)pu na podstawie wzgl(cid:218)dnej (cid:258)cie(cid:285)ki dost(cid:218)pu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Wy(cid:258)wietlanie wszystkich plików znajduj(cid:200)cych si(cid:218) w danym katalogu Jak to zrobi(cid:202)? Jak to zrobi(cid:202)? Implementowanie programu wyszukuj(cid:200)cego dane i dzia(cid:239)aj(cid:200)cego podobnie jak narz(cid:218)dzie grep Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Implementowanie programu automatycznie zmieniaj(cid:200)cego nazwy plików Jak to zrobi(cid:202)? Implementowanie programu obliczaj(cid:200)cego wielko(cid:258)(cid:202) katalogu Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Obliczanie danych statystycznych dotycz(cid:200)cych typów plików Jak to zrobi(cid:202)? Implementowanie narz(cid:218)dzia zmniejszaj(cid:200)cego wielko(cid:258)(cid:202) katalogu poprzez zast(cid:200)pienie powielonych plików do(cid:239)(cid:200)czeniami symbolicznymi Jak to zrobi(cid:202)? Jak to dzia(cid:239)a? Co dalej? Skorowidz 419 419 420 420 422 422 423 423 426 426 427 430 431 431 433 434 434 435 437 437 439 440 440 442 443 445 446 447 11 Poleć książkęKup książkę Poleć książkęKup książkę 1 Nowe funkcje w C++17 W tym rozdziale omówi(cid:218) nast(cid:218)puj(cid:200)ce receptury: (cid:81) U(cid:285)ycie strukturalnych wi(cid:200)za(cid:241) do rozpakowania warto(cid:258)ci zwrotnej (cid:81) Ograniczanie zasi(cid:218)gu zmiennej do konstrukcji if i switch (cid:81) Zalety stosowania nowych regu(cid:239) inicjalizacji z u(cid:285)yciem sk(cid:239)adni opartej na nawiasach (cid:81) Umo(cid:285)liwienie konstruktorowi automatycznego okre(cid:258)lenia typu klasy szablonu (cid:81) U(cid:285)ycie wyra(cid:285)enia constexpr-if do uproszczenia decyzji podejmowanych podczas kompilacji (cid:81) W(cid:239)(cid:200)czenie bibliotek w postaci samych nag(cid:239)ówków z u(cid:285)yciem osadzonych zmiennych (cid:81) Implementowanie za pomoc(cid:200) wyra(cid:285)e(cid:241) fold przydatnych funkcji pomocniczych Wprowadzenie J(cid:218)zyk C++ zyska(cid:239) wiele usprawnie(cid:241) wraz z wydaniem standardów C++11, C++14 i C++17. Obecnie jest to zupe(cid:239)nie inny j(cid:218)zyk w porównaniu do tego, którym by(cid:239) jeszcze dekad(cid:218) temu. Standard C++ nie dotyczy jedynie j(cid:218)zyka, który musi by(cid:202) obs(cid:239)ugiwany przez kompilatory, ale równie(cid:285) standardow(cid:200) bibliotek(cid:218) szablonów (ang. standard template library — STL). Na podstawie wielu przyk(cid:239)adów przedstawionych w tej ksi(cid:200)(cid:285)ce dowiesz si(cid:218), jak najlepiej wykorzysta(cid:202) bibliotek(cid:218) STL. W tym rozdziale skoncentruj(cid:218) si(cid:218) na najwa(cid:285)niejszych nowych funkcjach wprowadzonych w najnowszym wydaniu C++. Ich opanowanie bardzo pomo(cid:285)e w tworzeniu ekspresyjnego, (cid:239)atwego w odczycie i pó(cid:283)niejszej konserwacji kodu. Poleć książkęKup książkę C++17 STL. Receptury Zobaczysz, jak za pomoc(cid:200) strukturalnych wi(cid:200)za(cid:241) komfortowo uzyska(cid:202) dost(cid:218)p do poszczegól- nych elementów sk(cid:239)adowych par, krotek i struktur, a tak(cid:285)e jak ograniczy(cid:202) zasi(cid:218)g zmiennej, wykorzystuj(cid:200)c do tego nowe mo(cid:285)liwo(cid:258)ci w zakresie inicjalizacji zmiennej w konstrukcjach if i switch. Syntaktyczna dwuznaczno(cid:258)(cid:202) wprowadzona przez standard C++11 wraz z now(cid:200) sk(cid:239)adni(cid:200) inicjalizacji z u(cid:285)yciem nawiasu wyszukuj(cid:200)c(cid:200) ten sam inicjalizator dla list zosta(cid:239)a po- prawiona dzi(cid:218)ki nowym regu(cid:239)om inicjalizacji z u(cid:285)yciem sk(cid:239)adni opartej na nawiasach. Kon- kretny typ egzemplarza klasy szablonu mo(cid:285)e by(cid:202) ustalony na podstawie rzeczywistych argu- mentów konstruktora. Je(cid:285)eli poszczególne specjalizacje szablonu klasy b(cid:218)d(cid:200) powodowa(cid:239)y powstawanie zupe(cid:239)nie odmiennego kodu, teraz mo(cid:285)na to (cid:239)atwo wyrazi(cid:202) za pomoc(cid:200) wyra(cid:285)enia constexpr-if. Obs(cid:239)uga parametrów wariadycznych w funkcjach szablonu sta(cid:239)a si(cid:218) w wielu przypadkach znacznie (cid:239)atwiejsza dzi(cid:218)ki pojawieniu si(cid:218) nowych wyra(cid:285)e(cid:241) fold. Wreszcie znacznie bardziej komfortowe jest definiowanie statycznych, dost(cid:218)pnych globalnie obiektów w biblio- tekach w postaci jedynie nag(cid:239)ówków z u(cid:285)yciem nowych mo(cid:285)liwo(cid:258)ci deklarowania osadzonych zmiennych, co wcze(cid:258)niej by(cid:239)o mo(cid:285)liwe jedynie w funkcjach. Cz(cid:218)(cid:258)(cid:202) przyk(cid:239)adów w tym rozdziale mo(cid:285)e by(cid:202) bardziej interesuj(cid:200)ca dla programistów opraco- wuj(cid:200)cych biblioteki ni(cid:285) zajmuj(cid:200)cych si(cid:218) tworzeniem aplikacji. Cz(cid:218)(cid:258)(cid:202) wymienionych funkcji przedstawi(cid:239)em tutaj dla porz(cid:200)dku, nie musisz dok(cid:239)adnie zrozumie(cid:202) wszystkich przyk(cid:239)adów w tym rozdziale, aby móc opanowa(cid:202) materia(cid:239) zaprezentowany w pozosta(cid:239)ej cz(cid:218)(cid:258)ci ksi(cid:200)(cid:285)ki. U(cid:285)ycie strukturalnych wi(cid:200)za(cid:241) do rozpakowania warto(cid:258)ci zwrotnej Standard C++17 oferuje now(cid:200) funkcj(cid:218) (cid:239)(cid:200)cz(cid:200)c(cid:200) w sobie syntaktyczny lukier z automatycznym ustaleniem typu: strukturalne wi(cid:200)zanie. Pomaga ono w przypisywaniu poszczególnym zmiennym warto(cid:258)ci pochodz(cid:200)cych z par, krotek i struktur. W innych j(cid:218)zykach programowania to zadanie nosi nazw(cid:218) rozpakowania. Jak to zrobi(cid:202)? Zastosowanie strukturalnego wi(cid:200)zania w celu przypisania wielu warto(cid:258)ci pochodz(cid:200)cych z wi(cid:218)k- szej struktury zawsze ma posta(cid:202) pojedynczego kroku. Najpierw zobacz, jak taka operacja od- bywa(cid:239)a si(cid:218) przed wprowadzeniem standardu C++17. Nast(cid:218)pnie przejd(cid:218) do kilku przyk(cid:239)adów pokazuj(cid:200)cych, jak mo(cid:285)na to zrobi(cid:202) w C++17. (cid:81) Zaczynam od uzyskania dost(cid:218)pu do poszczególnych warto(cid:258)ci w std::pair. Przyjmuj(cid:218) za(cid:239)o(cid:285)enie o istnieniu funkcji matematycznej, divide_remainder(), która akceptuje dzieln(cid:200) (parametr dividend) i dzielnik (parametr divisor) oraz zwraca wynik dzielenia liczb wraz z reszt(cid:200). Wspomniane warto(cid:258)ci s(cid:200) zwracane za pomoc(cid:200) algorytmu std::pair: std::pair int, int divide_remainder(int dividend, int divisor); 22 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 Spójrz na sposób, w jaki uzyskujemy dost(cid:218)p do poszczególnych warto(cid:258)ci znajduj(cid:200)cych si(cid:218) w wyniku dzia(cid:239)ania funkcji: const auto result (divide_remainder(16, 3)); std::cout 16 / 3 to result.first plus reszta z dzielenia result.second \n ; Zamiast stosowa(cid:202) podej(cid:258)cie przedstawione powy(cid:285)ej poszczególne warto(cid:258)ci mo(cid:285)na przypisa(cid:202) zmiennym za pomoc(cid:200) ekspresyjnych nazw, które s(cid:200) znacznie (cid:239)atwiejsze w odczycie. Spójrz na ten fragment kodu: auto [fraction, remainder] = divide_remainder(16, 3); std::cout 16 / 3 to fraction plus reszta z dzielenia remainder \n ; (cid:81) Strukturalne wi(cid:200)zanie dzia(cid:239)a równie(cid:285) z algorytmem std::tuple. Przyjmujemy za(cid:239)o(cid:285)enie o istnieniu przedstawionej poni(cid:285)ej funkcji, która zwraca pobrane z internetu informacje o kursie akcji: std::tuple std::string, std::chrono::system_clock::time_point, unsigned stock_info(const std::string name); Przypisanie wyniku poszczególnym zmiennym odbywa si(cid:218) dok(cid:239)adnie tak samo jak w poprzednim przyk(cid:239)adzie: const auto [name, valid_time, price] = stock_info( INTC ); (cid:81) Strukturalne wi(cid:200)zanie sprawdza si(cid:218) równie(cid:285) w przypadku pracy z niestandardowymi strukturami. Przyjmuj(cid:218) za(cid:239)o(cid:285)enie o istnieniu nast(cid:218)puj(cid:200)cej struktury: struct employee { unsigned id; std::string name; std::string role; unsigned salary; }; Dost(cid:218)p do poszczególnych elementów sk(cid:239)adowych mo(cid:285)na uzyska(cid:202) za pomoc(cid:200) strukturalnego wi(cid:200)zania. Alternatywne podej(cid:258)cie polega na u(cid:285)yciu p(cid:218)tli, przy za(cid:239)o(cid:285)eniu o istnieniu wektora, jak pokaza(cid:239)em tutaj: int main() { std::vector employee employees { /* Inicjalizacja odby(cid:225)a si(cid:266) w innym miejscu */}; for (const auto [id, name, role, salary] : employees) { std::cout Nazwisko: name Stanowisko: role Wynagrodzenie: salary \n ; } } 23 Poleć książkęKup książkę C++17 STL. Receptury Jak to dzia(cid:239)a? Strukturalne wi(cid:200)zanie jest zawsze stosowane z u(cid:285)yciem nast(cid:218)puj(cid:200)cego wzorca: auto [zmienna1, zmienna2, ...] = wyra(cid:285)enie pary, krotki, struktury lub tablicy ; (cid:81) Lista zmiennych, zmienna1, zmienna2 itd., musi dok(cid:239)adnie odpowiada(cid:202) liczbie zmiennych znajduj(cid:200)cych si(cid:218) w wyra(cid:285)eniu, z którego b(cid:218)d(cid:200) pochodzi(cid:239)y przypisywane warto(cid:258)ci. (cid:81) Wyra(cid:285)enie pary, krotki, struktury lub tablicy musi mie(cid:202) jedn(cid:200) z nast(cid:218)puj(cid:200)cych postaci: (cid:81) Para: std::pair. (cid:81) Krotka: std::tuple. (cid:81) Struktura. (cid:191)aden element sk(cid:239)adowy nie mo(cid:285)e by(cid:202) statyczny, a wszystkie musz(cid:200) by(cid:202) zdefiniowane w tej samej klasie bazowej. Pierwszy zadeklarowany element sk(cid:239)adowy b(cid:218)dzie przypisany pierwszej zmiennej, drugi element sk(cid:239)adowy — drugiej zmiennej itd. (cid:81) Tablica o sta(cid:239)ej wielko(cid:258)ci. (cid:81) Typem mo(cid:285)e by(cid:202) auto, const auto, const auto lub nawet auto . Ze wzgl(cid:218)du mi(cid:218)dzy innymi na wydajno(cid:258)(cid:202) staraj si(cid:218) podczas u(cid:285)ywania odwo(cid:239)a(cid:241) zminimalizowa(cid:202) liczb(cid:218) niepotrzebnych operacji kopiowania. Je(cid:285)eli podasz zbyt du(cid:285)(cid:200) lub ma(cid:239)(cid:200) liczb(cid:218) zmiennych w nawiasie kwadratowym, wówczas kom- pilator wygeneruje komunikat b(cid:239)(cid:218)du: std::tuple int, float, long tup {1, 2.0, 3}; auto [a, b] = tup; // To polecenie nie dzia(cid:225)a Powy(cid:285)sze polecenie próbuje skopiowa(cid:202) dane z trzyelementowej krotki do jedynie dwóch zmiennych. Kompilator natychmiast wychwyci ten b(cid:239)(cid:200)d i wygeneruje odpowiedni komunikat: error: type std::tuple int, float, long decomposes into 3 elements, but only 2 names were provided auto [a, b] = tup; Co dalej? Za pomoc(cid:200) strukturalnych wi(cid:200)za(cid:241) mo(cid:285)na natychmiast uzyska(cid:202) dost(cid:218)p do wielu podstawowych struktur danych STL bez konieczno(cid:258)ci zmiany czegokolwiek. Na przyk(cid:239)ad tutaj przedstawi- (cid:239)em p(cid:218)tl(cid:218), która wy(cid:258)wietla wszystkie elementy zdefiniowane w egzemplarzu std::map: std::map std::string, size_t animal_population { { ludzi , 7000000000}, { kurczaków , 17863376000}, 24 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 { wielb(cid:239)(cid:200)dów , 24246291}, { owiec , 1086881528}, /* … */ }; for (const auto [species, count] : animal_population) { std::cout Mamy count (cid:285)yj(cid:200)cych na naszej planecie.\n ; } Ten konkretny przyk(cid:239)ad dzia(cid:239)a, poniewa(cid:285) w trakcie iteracji kontenera std::map powstaje w(cid:218)ze(cid:239) std::pair const typ_klucza, typ_warto(cid:258)ci dla ka(cid:285)dego kroku iteracji. Dok(cid:239)adniej mówi(cid:200)c, te w(cid:218)z(cid:239)y s(cid:200) rozpakowywane za pomoc(cid:200) funkcji wi(cid:200)zania — typ_klucza to ci(cid:200)g tekstowy b(cid:218)d(cid:200)- cy nazw(cid:200) populacji (string), natomiast typ_warto(cid:258)ci to wielko(cid:258)(cid:202) populacji podana jako size_t — w celu uzyskania do nich dost(cid:218)pu w tre(cid:258)ci p(cid:218)tli. Przed wprowadzeniem standardu C++17 podobny efekt mo(cid:285)na by(cid:239)o otrzyma(cid:202) po zastoso- waniu wywo(cid:239)ania std::tie(): int remainder; std::tie(std::ignore, remainder) = divide_remainder(16, 5); std::cout 16 5 to remainder \n ; Przyk(cid:239)ad ten pokazuje, jak par(cid:218) wynikow(cid:200) rozpakowa(cid:202) na posta(cid:202) dwóch zmiennych. Wywo(cid:239)a- nie std::tie() oferuje znacznie mniejsze mo(cid:285)liwo(cid:258)ci ni(cid:285) strukturalne wi(cid:200)zanie w tym sensie, (cid:285)e wszystkie zmienne, do których b(cid:218)d(cid:200) do(cid:239)(cid:200)czane warto(cid:258)ci, trzeba wcze(cid:258)niej zdefiniowa(cid:202). Jednak z drugiej strony przyk(cid:239)ad pokazuje zalet(cid:218) wywo(cid:239)ania std::tie() nieoferowan(cid:200) przez struktu- ralne wi(cid:200)zanie: warto(cid:258)(cid:202) std::ignore dzia(cid:239)a w charakterze imitacji warto(cid:258)ci. To b(cid:218)dzie warto(cid:258)(cid:202) reszty z dzielenia usuwana w tym przyk(cid:239)adzie, poniewa(cid:285) jest w tym miejscu niepotrzebna. Podczas u(cid:285)ywania strukturalnego wi(cid:200)zania nie ma imitacji warto(cid:258)ci, wi(cid:218)c wszystkie warto(cid:258)ci s(cid:200) przypi- sywane nazwanym zmiennym. Tego rodzaju operacja, a nast(cid:218)pnie zignorowanie niepotrzebnej warto(cid:258)ci, jest mimo tego efektywna, poniewa(cid:285) kompilator bardzo (cid:239)atwo potrafi zoptymalizowa(cid:202) nieu(cid:285)ywane wi(cid:200)zania. We wcze(cid:258)niejszych wydaniach standardu C++ funkcja divide_remainder() mog(cid:239)a by(cid:202) zaim- plementowana w nast(cid:218)puj(cid:200)cy sposób za pomoc(cid:200) parametrów danych wyj(cid:258)ciowych: bool divide_remainder(int dividend, int divisor, int fraction, int remainder); Uzyskanie dost(cid:218)pu do warto(cid:258)ci odbywa(cid:239)o si(cid:218) wówczas z u(cid:285)yciem kodu, takiego jak ten: int fraction, remainder; const bool success {divide_remainder(16, 3, fraction, remainder)}; if (success) { std::cout 16 / 3 to fraction plus reszta z dzielenia remainder \n ; } 25 Poleć książkęKup książkę C++17 STL. Receptury Wielu programistów nadal woli to podej(cid:258)cie zamiast zwrotu skomplikowanych struktur da- nych w stylu na przyk(cid:239)ad pary lub krotki. Uznaj(cid:200) oni, (cid:285)e przedstawiony kod b(cid:218)dzie dzia(cid:239)a(cid:239) szybciej ze wzgl(cid:218)du na unikni(cid:218)cie po(cid:258)redniego kopiowania warto(cid:258)ci. Jednak w nowoczesnych kompilatorach kopiowanie nie zachodzi, co oznacza zoptymalizowanie operacji. Pomijaj(cid:200)c brakuj(cid:200)ce mo(cid:285)liwo(cid:258)ci j(cid:218)zyka C, dostarczanie skomplikowanych struktur danych za pomoc(cid:200) warto(cid:258)ci zwrotnej by(cid:239)o przez d(cid:239)ugi czas uznawane za powoln(cid:200) operacj(cid:218) ze wzgl(cid:218)du na konieczno(cid:258)(cid:202) ini- cjalizacji obiektu, a nast(cid:218)pnie skopiowania warto(cid:258)ci do zmiennej, która po stronie wywo(cid:239)uj(cid:200)cego po- winna zawiera(cid:202) warto(cid:258)(cid:202) zwrotn(cid:200). Nowoczesne kompilatory oferuj(cid:200) optymalizacj(cid:218) warto(cid:258)ci zwrotnej (ang. return value optimization — RVO), co pozwala na pomini(cid:218)cie po(cid:258)rednich operacji kopiowania. Ograniczanie zasi(cid:218)gu zmiennej do konstrukcji if i switch W dobrym tonie jest maksymalne ograniczanie zasi(cid:218)gu zmiennej. Jednak czasami trzeba naj- pierw otrzyma(cid:202) warto(cid:258)(cid:202), która b(cid:218)dzie mog(cid:239)a by(cid:202) dalej przetwarzana tylko po spe(cid:239)nieniu okre- (cid:258)lonego warunku. Do tego celu standard C++17 oferuje konstrukcje if i switch wraz z odpowiednimi proce- durami inicjalizuj(cid:200)cymi. Jak to zrobi(cid:202)? W tej recepturze sk(cid:239)adni(cid:218) inicjalizatora wykorzystam w obu obs(cid:239)ugiwanych kontekstach, aby pokaza(cid:202), jak dzi(cid:218)ki niej kod mo(cid:285)e sta(cid:202) si(cid:218) znacznie bardziej przejrzysty. (cid:81) Konstrukcja if. Przyjmuj(cid:218) za(cid:239)o(cid:285)enie, (cid:285)e za pomoc(cid:200) metody find() egzemplarza std::map trzeba znale(cid:283)(cid:202) znak w mapowaniu znaków. if (auto itr (character_map.find(c)); itr != character_map.end()) { // Iterator *itr jest poprawny i zostanie wykorzystany do pewnych operacji } else { // Iterator itr jest iteratorem ko(cid:276)cowym; nie nale(cid:298)y przeprowadza(cid:252) dereferencji } // Iterator itr jest tutaj niedost(cid:266)pny (cid:81) Konstrukcja switch. Oto jak przedstawia si(cid:218) operacja pobrania znaku z danych wej(cid:258)ciowych i jednocze(cid:258)nie sprawdzenie warto(cid:258)ci w konstrukcji switch, na przyk(cid:239)ad w celu zachowania kontroli nad gr(cid:200): switch (char c (getchar()); c) { case a : move_left(); break; case s : move_back(); break; case w : move_fwd(); break; 26 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 case d : move_right(); break; case q : quit_game(); break; case 0 ... 9 : select_tool( 0 - c); break; default: std::cout Nieprawid(cid:239)owe dane wej(cid:258)ciowe: c \n ; } Jak to dzia(cid:239)a? Konstrukcje if i switch wraz z inicjalizatorami to w zasadzie lukier syntaktyczny. Dwa przed- stawione poni(cid:285)ej fragmenty kodu s(cid:200) swoimi odpowiednikami. Kod stosowany przed wydaniem standardu C++17: { auto var (init_value); if (condition) { // Zmienna var jest dost(cid:266)pna w ga(cid:225)(cid:266)zi A } else { // Zmienna var jest dost(cid:266)pna w ga(cid:225)(cid:266)zi B } // Zmienna var jest nadal dost(cid:266)pna } Kod stosowany od chwili wydania standardu C++17: if (auto var (init_value); condition) { // Zmienna var jest dost(cid:266)pna w ga(cid:225)(cid:266)zi A } else { // Zmienna var jest dost(cid:266)pna w ga(cid:225)(cid:266)zi B } // Zmienna var nie jest ju(cid:298) dost(cid:266)pna To samo ma zastosowanie w przypadku konstrukcji switch. Kod stosowany przed wydaniem standardu C++17: { auto var (init_value); switch (var) { case 1: ... case 2: ... ... } // Zmienna var jest nadal dost(cid:266)pna } 27 Poleć książkęKup książkę C++17 STL. Receptury Kod stosowany od chwili wydania standardu C++17: switch (auto var (init_value); var) { case 1: ... case 2: ... ... } // Zmienna var nie jest ju(cid:298) dost(cid:266)pna Ta funkcja jest niezwykle u(cid:285)yteczna i pozwala maksymalnie zmniejszy(cid:202) zasi(cid:218)g zmiennej. Przed wydaniem standardu C++17 osi(cid:200)gni(cid:218)cie pokazanego efektu by(cid:239)o mo(cid:285)liwe jedynie po uj(cid:218)ciu kodu w dodatkowy nawias klamrowy, co mo(cid:285)esz zobaczy(cid:202) w przyk(cid:239)adach zatytu(cid:239)owa- nych „Kod stosowany przed wydaniem standardu C++17”. Krótszy cykl (cid:285)yciowy zmniejsza liczb(cid:218) zmiennych w zasi(cid:218)gu, co z kolei przek(cid:239)ada si(cid:218) na wi(cid:218)ksz(cid:200) przejrzysto(cid:258)(cid:202) kodu i jego (cid:239)atwiejsz(cid:200) refaktoryzacj(cid:218). Co dalej? Kolejnym u(cid:285)ytecznym przypadkiem jest ograniczenie zasi(cid:218)gu selekcji o znaczeniu krytycznym. Spójrz na ten fragment kodu: if (std::lock_guard std::mutex lg {my_mutex}; some_condition) { // Dowolna operacja } W pierwszej chwili wydaje si(cid:218), (cid:285)e zostanie utworzony egzemplarz std::lock_guard. To jest klasa akceptuj(cid:200)ca egzemplarz mutex jako argument konstruktora. Ten kod powoduje na(cid:239)o(cid:285)enie na muteks blokady w konstruktorze, a po opuszczeniu przez niego zasi(cid:218)gu destruktor powo- duje zwolnienie tej blokady. Dzi(cid:218)ki temu praktycznie nie mo(cid:285)na zapomnie(cid:202) o odblokowaniu muteksu. Przed wydaniem standardu C++17 konieczne by(cid:239)o u(cid:285)ycie dodatkowej pary nawia- sów w celu ustalenia zasi(cid:218)gu i miejsca zwolnienia blokady. Innym interesuj(cid:200)cym przypadkiem jest zasi(cid:218)g s(cid:239)abych wska(cid:283)ników. Spójrz na nast(cid:218)puj(cid:200)cy fragment kodu: if (auto shared_pointer (weak_pointer.lock()); shared_pointer != nullptr) { // Wspó(cid:225)dzielony obiekt nadal istnieje } else { // Zmienna shared_pointer jest dost(cid:266)pna, cho(cid:252) to b(cid:266)dzie wska(cid:296)nik typu null } // Zmienna shared_pointer nie jest ju(cid:298) dost(cid:266)pna To jest kolejny przyk(cid:239)ad, w którym zmienna (tutaj shared_pointer) niepotrzebnie wycieka do bie(cid:285)(cid:200)cego zasi(cid:218)gu. Ta zmienna okazuje si(cid:218) zupe(cid:239)nie bezu(cid:285)yteczna poza blokiem konstrukcji warunkowej if. Konstrukcja if wraz z inicjalizatorem jest szczególnie u(cid:285)yteczna podczas pracy z przestarza- (cid:239)ym API wykorzystuj(cid:200)cym parametry danych wyj(cid:258)ciowych: 28 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 if (DWORD exit_code; GetExitCodeProcess(process_handle, exit_code)) { std::cout Kod wyj(cid:258)cia procesu to: exit_code \n ; } // Poza konstrukcj(cid:261) warunkow(cid:261) if zmienna exit_code jest nieprzydatna GetExitCodeProcess() to funkcja API j(cid:200)dra systemu Windows. Jej warto(cid:258)ci(cid:200) zwrotn(cid:200) jest kod wyj(cid:258)cia danego procesu, ale tylko wtedy, gdy uchwyt do procesu jest prawid(cid:239)owy. Po opusz- czeniu bloku konstrukcji warunkowej zmienna exit_code jest bezu(cid:285)yteczna i dlatego nie po- trzebujemy jej ju(cid:285) w (cid:285)adnym zasi(cid:218)gu. Mo(cid:285)liwo(cid:258)(cid:202) inicjalizacji zmiennych w blokach konstrukcji if okazuje si(cid:218) bardzo u(cid:285)yteczna w wielu sytuacjach, zw(cid:239)aszcza podczas pracy z ju(cid:285) przestarza(cid:239)ymi API, które u(cid:285)ywaj(cid:200) para- metrów danych wyj(cid:258)ciowych. Staraj si(cid:218) maksymalnie ogranicza(cid:202) zakres w poleceniach inicjalizacyjnych konstrukcji if i switch. Dzi(cid:218)ki temu tworzony kod b(cid:218)dzie zwi(cid:218)(cid:283)lejszy, (cid:239)atwiejszy w odczycie, a podczas refaktoryzacji b(cid:218)dzie mo(cid:285)na (cid:239)atwiej nim operowa(cid:202). Zalety stosowania nowych regu(cid:239) inicjalizacji z u(cid:285)yciem sk(cid:239)adni opartej na nawiasach Wraz z wydaniem standardu C++11 pojawi(cid:239)a si(cid:218) nowa sk(cid:239)adnia inicjalizatora, wykorzystuj(cid:200)ca nawias klamrowy {}. Jej przeznaczeniem by(cid:239)o umo(cid:285)liwienie agregowania inicjalizacji, a tak(cid:285)e u(cid:285)ywania zwyk(cid:239)ych wywo(cid:239)a(cid:241) konstruktora. Niestety zbyt (cid:239)atwo mo(cid:285)na by(cid:239)o wyrazi(cid:202) nie to, co trzeba, w przypadku po(cid:239)(cid:200)czenia nowej sk(cid:239)adni z typem zmiennej auto. W standardzie C++17 wprowa- dzono rozbudowany zestaw regu(cid:239) inicjalizatora. W tej recepturze poka(cid:285)(cid:218), jak prawid(cid:239)owo za- inicjalizowa(cid:202) zmienne, wykorzystuj(cid:200)c do tego sk(cid:239)adni(cid:218) wprowadzon(cid:200) w standardzie C++17. Jak to zrobi(cid:202)? Zmienna jest inicjalizowana w jednym kroku. Stosuj(cid:200)c sk(cid:239)adni(cid:218) inicjalizatora, mamy dwie odmienne sytuacje: (cid:81) U(cid:285)ycie opartej na nawiasach sk(cid:239)adni inicjalizacji bez okre(cid:258)lenia typu auto: // Trzy identyczne sposoby zainicjalizowania zmiennej typu int int x1 = 1; int x2 {1}; int x3 (1); std::vector int v1 {1, 2, 3}; // Wektor wraz z trzema liczbami ca(cid:225)kowitymi: 1, 2, 3 std::vector int v2 = {1, 2, 3}; // To samo co powy(cid:298)ej std::vector int v3 (10, 20); // Wektor wraz z dziesi(cid:266)cioma liczbami ca(cid:225)kowitymi, // z których ka(cid:298)da ma warto(cid:286)(cid:252) 20 29 Poleć książkęKup książkę C++17 STL. Receptury (cid:81) U(cid:285)ycie opartej na nawiasach sk(cid:239)adni inicjalizacji z okre(cid:258)leniem typu auto: auto v {1}; // Zmienna v jest typu int auto w {1, 2}; // B(cid:225)(cid:261)d: w bezpo(cid:286)redniej inicjalizacji auto dozwolony // jest tylko jeden element! (to jest nowo(cid:286)(cid:252)) auto x = {1}; // Zmienna x ma posta(cid:252) std::initializer_list int auto y = {1, 2}; // Zmienna y ma posta(cid:252) std::initializer_list int auto z = {1, 2, 3.0}; // B(cid:225)(cid:261)d: nie mo(cid:298)na ustali(cid:252) typu elementu Jak to dzia(cid:239)a? Bez ustalania typu auto dzia(cid:239)anie operatora {} nie powinno by(cid:202) zaskoczeniem, przynajmniej podczas inicjalizowania warto(cid:258)ci zwyk(cid:239)ych typów. Z kolei w trakcie inicjalizowania kontene- rów, takich jak std::vector, std::list itd., ten inicjalizator spowoduje dopasowanie kon- struktora std::initializer_list klasy kontenera. To si(cid:218) odbywa w zach(cid:239)anny sposób, co oznacza brak mo(cid:285)liwo(cid:258)ci dopasowania konstruktorów nieagreguj(cid:200)cych (konstruktor nieagre- guj(cid:200)cy to zwyk(cid:239)y konstruktor b(cid:218)d(cid:200)cy przeciwie(cid:241)stwem akceptuj(cid:200)cego list(cid:218) inicjalizatora). Na przyk(cid:239)ad std::vector oferuje konstruktor nieagreguj(cid:200)cy, który wype(cid:239)nia dowoln(cid:200) liczb(cid:218) elementów t(cid:200) sam(cid:200) warto(cid:258)ci(cid:200): std::vector int v (N, warto(cid:258)(cid:202)). W przypadku polecenia std::vector int v {N, warto(cid:258)(cid:202)} zostanie u(cid:285)yty konstruktor initializer_list inicjalizuj(cid:200)cy wektor wraz z dwoma elementami: N i warto(cid:258)(cid:202). To jest zachowanie, o którym nale(cid:285)y wiedzie(cid:202). Warto równie(cid:285) pami(cid:218)ta(cid:202) o pewnym drobiazgu dotycz(cid:200)cym operatora {} w porównaniu do wywo(cid:239)ania konstruktora za pomoc(cid:200) zwyk(cid:239)ego nawiasu (), a mianowicie braku niejawnej kon- wersji typu. W przypadku wywo(cid:239)a(cid:241) int x (1.2); i int x = 1.2; otrzymamy zmienn(cid:200) x zaini- cjalizowan(cid:200) wraz z warto(cid:258)ci(cid:200) 1. Warto(cid:258)(cid:202) zmiennoprzecinkowa zosta(cid:239)a zaokr(cid:200)glona w dó(cid:239) i skonwertowana na posta(cid:202) liczby ca(cid:239)kowitej. Natomiast wywo(cid:239)anie int x {1.2}; uniemo(cid:285)liwia kompilacj(cid:218) z powodu próby (cid:258)cis(cid:239)ego dopasowania typu konstruktora. Mo(cid:285)na si(cid:218) spiera(cid:202), który z przedstawionych powy(cid:285)ej stylów inicjalizacji jest najlepszy. Fani sk(cid:239)adni inicjalizacji opartej na nawiasie klamrowym uwa(cid:285)aj(cid:200), (cid:285)e takie rozwi(cid:200)zanie konkretnie wskazuje typ zmiennej inicjalizowanej za pomoc(cid:200) wywo(cid:239)ania konstruktora, a tego rodzaju wiersz kodu nie prze- prowadza ponownej inicjalizacji czegokolwiek. Ponadto u(cid:285)ycie nawiasu klamrowego spowoduje wybór jedynie dopasowanego konstruktora, podczas gdy sk(cid:239)adnia inicjalizacji opartej na nawiasie zwyk(cid:239)ym próbuje dopasowa(cid:202) najbli(cid:285)szy konstruktor — w tym celu mo(cid:285)e nawet przeprowadzi(cid:202) konwersj(cid:218) typu. Dodatkowa regu(cid:239)a wprowadzona w standardzie C++17 wp(cid:239)ywa na inicjalizacj(cid:218) wraz z okre- (cid:258)laniem typu auto. W C++11 nast(cid:200)pi poprawne ustalenie typu zmiennej auto x {123}; jako std::initializer_list int z tylko jednym elementem, ale rzadko o to chodzi(cid:239)o. Natomiast w standardzie C++17 to samo polecenie powoduje, (cid:285)e zmienna x jest typu int. 30 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 Pami(cid:218)taj o nast(cid:218)puj(cid:200)cych regu(cid:239)ach: (cid:81) Wywo(cid:239)anie auto zmienna {jeden_element}; okre(cid:258)la zmienn(cid:200) jako takiego samego typu jak jeden_element. (cid:81) Wywo(cid:239)anie auto zmienna {element1, element2, ...}; jest nieprawid(cid:239)owe i uniemo(cid:285)liwia kompilacj(cid:218) kodu. (cid:81) Wywo(cid:239)anie auto zmienna = {element1, element2, ...}; okre(cid:258)la typ zmiennej jako std::initializer_list T , gdzie T to taki sam typ, jaki maj(cid:200) wszystkie elementy listy. Standard C++17 znacznie utrudnia przypadkowe zdefiniowanie inicjalizatora listy. Wypróbowanie przedstawionych fragmentów kodu z ró(cid:285)nymi kompilatorami dzia(cid:239)aj(cid:200)cymi w trybach zgodno(cid:258)ci z C++11 i C++14 poka(cid:285)e, (cid:285)e niektóre z nich faktycznie okre(cid:258)laj(cid:200) zmienn(cid:200) x w wywo(cid:239)aniu auto x {123}; jako typu int, podczas gdy inne — jako typu std::initializer_list int . Tworzenie kodu w taki sposób mo(cid:285)e prowadzi(cid:202) do problemów z jego przenoszeniem. Umo(cid:285)liwienie konstruktorowi automatycznego okre(cid:258)lenia typu klasy szablonu Wiele klas w C++ jest zwykle specjalizowanych dla okre(cid:258)lonych typów, które mog(cid:200) by(cid:202) (cid:239)atwo okre(cid:258)lane na podstawie typu zmiennych umieszczonych w wywo(cid:239)aniach konstruktorów. Przed wydaniem C++17 to nie by(cid:239)a ustandaryzowana funkcja. C++17 pozwala kompilatorowi na automatyczne okre(cid:258)lanie typu klasy szablonu na podstawie wywo(cid:239)ania konstruktora. Jak to zrobi(cid:202)? Bardzo dobrym przyk(cid:239)adem jest tworzenie egzemplarzy typu std::pair i std::tuple. Mog(cid:200) by(cid:202) one specjalizowane i tworzone w jednym kroku: std::pair my_pair (123, abc ); // std::pair int, const char* std::tuple my_tuple (123, 12.3, abc ); // std::tuple int, double, const char* Jak to dzia(cid:239)a? Oto przyk(cid:239)ad definicji klasy z automatycznym okre(cid:258)laniem typu szablonu: template typename T1, typename T2, typename T3 class my_wrapper { T1 t1; 31 Poleć książkęKup książkę C++17 STL. Receptury T2 t2; T3 t3; public: explicit my_wrapper(T1 t1_, T2 t2_, T3 t3_) : t1{t1_}, t2{t2_}, t3{t3_} {} /* … */ }; Przedstawiony powy(cid:285)ej fragment kodu to tylko jeszcze jedna klasa szablonu. W celu utwo- rzenia jej egzemplarza wcze(cid:258)niej trzeba by(cid:239)o u(cid:285)ywa(cid:202) nast(cid:218)puj(cid:200)cego polecenia: my_wrapper int, double, const char * wrapper {123, 1.23, abc }; Natomiast teraz mo(cid:285)na pomin(cid:200)(cid:202) fragment dotycz(cid:200)cy specjalizacji szablonu: my_wrapper wrapper {123, 1.23, abc }; Przed wydaniem standardu C++17 takie podej(cid:258)cie by(cid:239)o mo(cid:285)liwe jedynie poprzez utworzenie funkcji pomocniczej: my_wrapper T1, T2, T3 make_wrapper(T1 t1, T2 t2, T3 t3) { return {t1, t2, t3}; } Wykorzystuj(cid:200)c tak(cid:200) funkcj(cid:218) pomocnicz(cid:200), mo(cid:285)na by(cid:239) osi(cid:200)gn(cid:200)(cid:202) efekt podobny do poprzedniego, czyli pomin(cid:200)(cid:202) fragment dotycz(cid:200)cy specjalizacji szablonu, jak tutaj: auto wrapper (make_wrapper(123, 1.23, abc )); Biblioteka STL jest dostarczana z wieloma funkcjami pomocniczymi, takimi jak std::make_shared(), std::make_unique(), std::make_tuple() itd. W specyfikacji C++17 wi(cid:218)kszo(cid:258)(cid:202) z nich mo(cid:285)na uzna(cid:202) za zb(cid:218)dne. Oczywi(cid:258)cie pozosta(cid:239)y one w celu zapewnienia wstecznej zgodno(cid:258)ci. Co dalej? Przed chwil(cid:200) do(cid:258)(cid:202) dok(cid:239)adnie przedstawi(cid:239)em niejawne okre(cid:258)lanie typu szablonu. Jednak w pewnych sytuacjach nie mo(cid:285)na opiera(cid:202) si(cid:218) na niejawnym okre(cid:258)laniu typu. Spójrz na ten oto fragment kodu: template typename T struct sum { T value; template typename ... Ts sum(Ts ... values) : value{(values + ...)} {} }; 32 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 Struktura sum akceptuje dowoln(cid:200) liczb(cid:218) parametrów i dodaje je do siebie za pomoc(cid:200) wyra(cid:285)e- nia fold (w dalszej cz(cid:218)(cid:258)ci rozdzia(cid:239)u przedstawi(cid:218) nieco wi(cid:218)cej informacji na temat takich wyra- (cid:285)e(cid:241)). Obliczona suma b(cid:218)dzie zapisana w zmiennej sk(cid:239)adowej o nazwie value. Móg(cid:239)by(cid:258) w tym miejscu zapyta(cid:202), jakiego typu jest T. Je(cid:285)eli nie chcesz wyra(cid:283)nie zdefiniowa(cid:202) typu T, b(cid:218)dzie on zale(cid:285)a(cid:239) od typu warto(cid:258)ci przekazanych konstruktorowi. W przypadku ci(cid:200)gów tekstowych ty- pem T b(cid:218)dzie std::string. Je(cid:285)eli konstruktor otrzyma liczby ca(cid:239)kowite, typem b(cid:218)dzie int. Natomiast w przypadku dostarczenia konstruktorowi liczb ca(cid:239)kowitych, zmiennoprzecinko- wych i podwójnej precyzji kompilator musi wybra(cid:202) odpowiedni typ umo(cid:285)liwiaj(cid:200)cy przecho- wywanie tych warto(cid:258)ci bez utraty jakiejkolwiek informacji. Aby osi(cid:200)gn(cid:200)(cid:202) ten cel, mo(cid:285)na za- stosowa(cid:202) podpowied(cid:283) niejawnego okre(cid:258)lania typu: template typename ... Ts sum(Ts ... ts) - sum std::common_type_t Ts... ; Podpowied(cid:283) niejawnego okre(cid:258)lania typu wskazuje kompilatorowi mo(cid:285)liwo(cid:258)(cid:202) u(cid:285)ycia egzem- plarza std::common_type w celu ustalenia wspólnego typu obejmuj(cid:200)cego wszystkie otrzymane warto(cid:258)ci. Spójrz na przyk(cid:239)ad u(cid:285)ycia podpowiedzi niejawnego okre(cid:258)lania typu: sum s {1u, 2.0, 3, 4.0f}; sum string_sum {std::string{ abc }, def }; std::cout s.value \n string_sum.value \n ; W pierwszym wierszu nast(cid:218)puje utworzenie obiektu sum wraz z argumentami konstruktora typów unsigned, double, int i float. W wyniku u(cid:285)ycia std::common_type_t kompilator wybie- ra double jako wspólny typ obejmuj(cid:200)cy warto(cid:258)ci przekazane konstruktorowi, wi(cid:218)c otrzymu- jemy egzemplarz sum double . W drugim wierszu przekazywany jest egzemplarz std::string i ci(cid:200)g tekstowy w stylu C. Kieruj(cid:200)c si(cid:218) podpowiedzi(cid:200) niejawnego okre(cid:258)lania typu, kompilator przygotowuje egzemplarz typu sum std::string . Po uruchomieniu powy(cid:285)szego fragmentu kodu nast(cid:200)pi wygenerowanie warto(cid:258)ci liczbowej 10 i ci(cid:200)gu tekstowego abcdef. U(cid:285)ycie wyra(cid:285)enia constexpr-if do uproszczenia decyzji podejmowanych podczas kompilacji W kodzie szablonu bardzo cz(cid:218)sto zachodzi potrzeba odmiennego wykonywania pewnych za- da(cid:241) w zale(cid:285)no(cid:258)ci od typu szablonu, dla którego jest on specjalizowany. Standard C++17 ofe- ruje wyra(cid:285)enie constexpr-if, które potrafi w takich sytuacjach znacznie upro(cid:258)ci(cid:202) kod. 33 Poleć książkęKup książkę C++17 STL. Receptury Jak to zrobi(cid:202)? W tej recepturze zaimplementuj(cid:218) niewielk(cid:200) funkcj(cid:218) pomocnicz(cid:200) szablonu klasy. Jest ona przeznaczona do pracy z ró(cid:285)nymi specjalizacjami typu szablonu, poniewa(cid:285) potrafi wybra(cid:202) zu- pe(cid:239)nie inny fragment kodu w zale(cid:285)no(cid:258)ci od typu specjalizacji. 1. Prac(cid:218) nale(cid:285)y rozpocz(cid:200)(cid:202) od utworzenia kodu niezale(cid:285)nego od specjalizacji. W omawianym przyk(cid:239)adzie to jest prosta klasa umo(cid:285)liwiaj(cid:200)ca dodanie warto(cid:258)ci typu U do warto(cid:258)ci elementu sk(cid:239)adowego typu T za pomoc(cid:200) funkcji add(): template typename T class addable { T val; public: addable(T v) : val{v} {} template typename U T add(U x) const { return val + x; } }; 2. Przyjmuj(cid:218) za(cid:239)o(cid:285)enie, (cid:285)e typem T jest std::vector cokolwiek , natomiast typem U jest int. Jaki powinien by(cid:202) wynik dodania liczby ca(cid:239)kowitej do wektora? Powiedzmy, (cid:285)e chc(cid:218) doda(cid:202) t(cid:218) liczb(cid:218) ca(cid:239)kowit(cid:200) do ka(cid:285)dego elementu wektora. Tak(cid:200) operacj(cid:218) przeprowadzam za pomoc(cid:200) p(cid:218)tli: template typename U T add(U x) { auto copy (val); // Pobranie kopii elementu sk(cid:225)adowego wektora for (auto n : copy) { n += x; } return copy; } 3. Nast(cid:218)pnym i zarazem ostatnim krokiem jest po(cid:239)(cid:200)czenie obu (cid:258)wiatów. Je(cid:285)eli T jest wektorem elementów U, wówczas nale(cid:285)y zastosowa(cid:202) wariant oparty na p(cid:218)tli, w przeciwnym razie wystarczy zaimplementowa(cid:202) zwyk(cid:239)(cid:200) operacj(cid:218) dodawania: template typename U T add(U x) const { if constexpr (std::is_same_v T, std::vector U ) { auto copy (val); for (auto n : copy) { n += x; } return copy; } else { return val + x; } } 34 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 4. Klasa jest teraz gotowa do u(cid:285)ycia. Spójrz, jak elegancko potrafi wspó(cid:239)pracowa(cid:202) z ró(cid:285)nymi typami danych, takimi jak int, float, std::vector int i std::vector string : addable int {1}.add(2); // Wynik wynosi 3 addable float {1.0}.add(2); // Wynik wynosi 3.0 addable std::string { aa }.add( bb ); // Wynik wynosi aabb std::vector int v {1, 2, 3}; addable std::vector int {v}.add(10); // Wynik wynosi std::vector int {11, 12, 13} std::vector std::string sv { a , b , c }; addable std::vector std::string {sv}.add(std::string{ z }); // Wynik wynosi { az , bz , cz } Jak to dzia(cid:239)a? Nowe wyra(cid:285)enie constexpr-if dzia(cid:239)a tak samo jak zwyk(cid:239)a konstrukcja warunkowa if-else. Ró(cid:285)nica mi(cid:218)dzy nimi polega na tym, (cid:285)e w przypadku constexpr-if warunek b(cid:218)dzie spraw- dzony podczas kompilacji. Generowany przez kompilator kod uruchomieniowy programu nie b(cid:218)dzie zawiera(cid:239) (cid:285)adnych polece(cid:241) ga(cid:239)(cid:218)zi constexpr-if. Innymi s(cid:239)owy: mo(cid:285)na pokusi(cid:202) si(cid:218) o stwierdzenie, (cid:285)e wyra(cid:285)enie to dzia(cid:239)a podobnie jak makra preprocesora #if i #else. Jednak w przypadku tych makr kod nie musi by(cid:202) nawet doskonale sformatowany syntaktycznie. Na- tomiast wszystkie ga(cid:239)(cid:218)zie w konstrukcji constexpr-if musz(cid:200) by(cid:202) doskonale sformatowane syntaktycznie, cho(cid:202) jednocze(cid:258)nie ga(cid:239)(cid:218)zie nie musz(cid:200) by(cid:202) syntaktycznie poprawne. W celu odró(cid:285)nienia kodu odpowiedzialnego za dodanie warto(cid:258)ci zmiennej x do wektora wy- korzystam typ std::is_same. Wyra(cid:285)enie std::is_same A, B ::value przyjmuje boolowsk(cid:200) warto(cid:258)(cid:202) true, gdy A i B s(cid:200) tego samego typu. Warunek wykorzystany w tej recepturze ma po- sta(cid:202) std::is_same T, std::vector U ::value i przyjmuje warto(cid:258)(cid:202) true, nawet je(cid:258)li u(cid:285)ytkow- nik specjalizuje klas(cid:218) jako T = std::vector X i próbuje wywo(cid:239)a(cid:202) funkcj(cid:218) add() wraz z para- metrem U = X. Oczywi(cid:258)cie pojedynczy blok constexpr-if-else mo(cid:285)e zawiera(cid:202) wiele polece(cid:241). (Zwró(cid:202) uwag(cid:218), (cid:285)e warto(cid:258)ci a i b zale(cid:285)(cid:200) od parametrów szablonu, a nie tylko od sta(cid:239)ych w trakcie kompilacji). if constexpr (a) { // Dowolna operacja } else if constexpr (b) { // Inna dowolna operacja } else { // Jeszcze inna dowolna operacja } W standardzie C++17 mamy wiele zwi(cid:200)zanych z metaprogramowaniem sytuacji, w których utworzenie kodu i jego pó(cid:283)niejszy odczyt b(cid:218)d(cid:200) (cid:239)atwiejsze, je(cid:258)li u(cid:285)yjesz wyra(cid:285)enia constexpr-if. 35 Poleć książkęKup książkę C++17 STL. Receptury Co dalej? Aby si(cid:218) przekona(cid:202), jak du(cid:285)ym usprawnieniem w j(cid:218)zyku C++ jest konstrukcja constexpr-if, spójrz na t(cid:218) sam(cid:200) implementacj(cid:218) przygotowan(cid:200) jeszcze przed wprowadzeniem standardu C++17: template typename T class addable { T val; public: addable(T v) : val{v} {} template typename U std::enable_if_t !std::is_same T, std::vector U ::value, T add(U x) const { return val + x; } template typename U std::enable_if_t std::is_same T, std::vector U ::value, std::vector U add(U x) const { auto copy (val); for (auto n : copy) { n += x; } return copy; } }; Wprawdzie bez u(cid:285)ycia wyra(cid:285)enia constexpr-if powy(cid:285)sza klasa b(cid:218)dzie obs(cid:239)ugiwa(cid:239)a wszystkie interesuj(cid:200)ce nas typy, ale jej kod jest niepotrzebnie skomplikowany. Jak to rozwi(cid:200)zanie dzia(cid:239)a w praktyce? Sama implementacja dwóch ró(cid:285)nych funkcji add() wygl(cid:200)da na prost(cid:200). Natomiast niezwykle skom- plikowanie przedstawiaj(cid:200) si(cid:218) deklaracje ich warto(cid:258)ci zwrotnych wraz ze sztuczk(cid:200) w postaci wy- ra(cid:285)enia takiego jak std::enable_if_t warunek, typ , które b(cid:218)dzie podanego typu, je(cid:258)li warunek przyjmie warto(cid:258)(cid:202) true. W przeciwnym razie std::enable_if_t nie okre(cid:258)li niczego. Normalnie taka sytuacja jest uznawana za b(cid:239)(cid:200)d, ale ju(cid:285) wkrótce dowiesz si(cid:218), dlaczego tutaj tak si(cid:218) nie dzieje. W przypadku drugiej funkcji add() ten sam warunek jest u(cid:285)ywany w odwrotny sposób. Dlatego w danej sytuacji tylko jedna z dwóch istniej(cid:200)cych implementacji mo(cid:285)e przyj(cid:200)(cid:202) warto(cid:258)(cid:202) true. Kiedy kompilator napotyka ró(cid:285)ne funkcje szablonu o tej samej nazwie i musi wybra(cid:202) tylko jedn(cid:200) z nich, do gry wchodzi wa(cid:285)na regu(cid:239)a okre(cid:258)lana mianem SFINAE (ang. substitution failure is not an error), co oznacza: niepowodzenie podczas podstawiania nie jest b(cid:239)(cid:218)dem. W takim przypadku kompilator nie wygeneruje b(cid:239)(cid:218)du, je(cid:258)li warto(cid:258)(cid:202) zwrotna dowolnej z wymienionych funkcji nie b(cid:218)dzie mog(cid:239)a by(cid:202) ustalona na podstawie nieprawid(cid:239)owego wyra(cid:285)enia szablonu (którym tutaj jest std::enable_if, poniewa(cid:285) jego warunek przyjmuje warto(cid:258)(cid:202) false). Kompi- lator po prostu szuka dalej i wypróbuje inne implementacje funkcji. Na tym polega sztuczka, dzi(cid:218)ki której dzia(cid:239)a przedstawione rozwi(cid:200)zanie. 36 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 Mnóstwo z tym k(cid:239)opotu. To doskonale pokazuje, (cid:285)e przedstawione zadanie mo(cid:285)na znacznie (cid:239)atwiej wykona(cid:202) dzi(cid:218)ki u(cid:285)yciu kodu zgodnego ze standardem C++17. W(cid:239)(cid:200)czenie bibliotek w postaci samych nag(cid:239)ówków z u(cid:285)yciem osadzonych zmiennych W j(cid:218)zyku C++ zawsze istnia(cid:239)a mo(cid:285)liwo(cid:258)(cid:202) deklarowania poszczególnych funkcji jako osadzo- nych. Standard C++17 pozwala deklarowa(cid:202) zmienne jako osadzone. Dzi(cid:218)ki temu mo(cid:285)na znacznie (cid:239)atwiej zaimplementowa(cid:202) biblioteki w postaci samych nag(cid:239)ówków, co wcze(cid:258)niej wymaga(cid:239)o stosowania sztuczek. Jak to zrobi(cid:202)? W tej recepturze utworz(cid:218) prost(cid:200) klas(cid:218), która mo(cid:285)e dzia(cid:239)a(cid:202) w charakterze elementu sk(cid:239)adowe- go typowej biblioteki w postaci samych nag(cid:239)ówków. Celem jest dostarczenie statycznego ele- mentu sk(cid:239)adowego i zainicjalizowanie go globalnie za pomoc(cid:200) s(cid:239)owa kluczowego inline. Takie podej(cid:258)cie by(cid:239)o niemo(cid:285)liwe przed wydaniem standardu C++17. 1. Klasa process_monitor powinna zawiera(cid:202) statyczne elementy sk(cid:239)adowe i jednocze(cid:258)nie by(cid:202) dost(cid:218)pna globalnie. Takie rozwi(cid:200)zanie wymaga(cid:239)oby podwójnego definiowania symboli. // foo_lib.hpp class process_monitor { public: static const std::string standard_string { pewien statyczny dost(cid:218)pny globalnie ci(cid:200)g tekstowy }; }; process_monitor global_process_monitor; 2. Je(cid:285)eli teraz plik zawieraj(cid:200)cy powy(cid:285)sz(cid:200) klas(cid:218) b(cid:218)dzie do(cid:239)(cid:200)czony w wielu plikach .cpp, aby przeprowadzi(cid:202) ich kompilacj(cid:218) i linkowanie, wówczas etap linkowania zako(cid:241)czy si(cid:218) niepowodzeniem. Rozwi(cid:200)zaniem problemu jest dodanie s(cid:239)owa kluczowego inline: // foo_lib.hpp class process_monitor { public: static const inline std::string standard_string { pewien statyczny dost(cid:218)pny globalnie ci(cid:200)g tekstowy }; }; inline process_monitor global_process_monitor; Voilà, o to chodzi(cid:239)o! 37 Poleć książkęKup książkę C++17 STL. Receptury Jak to dzia(cid:239)a? Program w C++ cz(cid:218)sto sk(cid:239)ada si(cid:218) z wielu plików kodu (cid:283)ród(cid:239)owego (maj(cid:200) one rozszerzenie .cpp lub .cc). Najcz(cid:218)(cid:258)ciej s(cid:200) one pojedynczo komplikowane na posta(cid:202) plików modu(cid:239)ów lub obiektów (w takim przypadku rozszerzeniem pliku jest .o). Ostatnim krokiem jest linkowanie wszystkich plików modu(cid:239)ów i obiektów w celu wygenerowania pojedynczego pliku wykony- walnego b(cid:200)d(cid:283) biblioteki wspó(cid:239)dzielonej lub statycznej. Je(cid:285)eli na etapie linkowania definicja danego symbolu zostanie znaleziona wielokrotnie, wów- czas mamy do czynienia z b(cid:239)(cid:218)dem. Przyjmuj(cid:218) za(cid:239)o(cid:285)enie o istnieniu funkcji z sygnatur(cid:200) tak(cid:200) jak int foo();. Kiedy dwa modu(cid:239)y definiuj(cid:200) t(cid:218) sam(cid:200) funkcj(cid:218), która z nich jest prawid(cid:239)owa? Linker nie mo(cid:285)e wybiera(cid:202) losowo implementacji. Có(cid:285), móg(cid:239)by, ale jest ma(cid:239)o prawdopodobne, aby takiego podej(cid:258)cia oczekiwa(cid:239) jakikolwiek programista. Tradycyjnym sposobem dostarczania globalnie dost(cid:218)pnych funkcji jest ich deklarowanie w plikach nag(cid:239)ówkowych, które mog(cid:200) by(cid:202) do(cid:239)(cid:200)czane przez dowolny modu(cid:239) C++ potrze- buj(cid:200)cy tych funkcji. Definicja ka(cid:285)dej funkcji dost(cid:218)pnej globalnie b(cid:218)dzie umieszczona raz w oddzielnym pliku modu(cid:239)u. Nast(cid:218)pnie te pliki b(cid:218)d(cid:200) linkowane wraz z modu(cid:239)ami, w których maj(cid:200) by(cid:202) u(cid:285)ywane dane funkcje. To podej(cid:258)cie jest okre(cid:258)lane mianem regu(cid:239)y ODR (ang. one definition rule), czyli regu(cid:239)y pojedynczej definicji. W sposób graficzny t(cid:218) regu(cid:239)(cid:218) pokaza(cid:239)em na rysunku 1.1. Rysunek 1.1. Regu(cid:239)a pojedynczej definicji 38 Poleć książkęKup książkę Rozdzia(cid:225) 1. • Nowe funkcje w C++17 Je(cid:285)eli to by(cid:239)oby jedyne rozwi(cid:200)zanie, wówczas zabrak(cid:239)oby mo(cid:285)liwo(cid:258)ci dostarczania bibliotek w postaci samych nag(cid:239)ówków. Biblioteki okazuj(cid:200) si(cid:218) niezwykle u(cid:285)yteczne, poniewa(cid:285) mog(cid:200) by(cid:202) do(cid:239)(cid:200)czane w dowolnym pliku programu C++ za pomoc(cid:200) poleceni
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

C++17 STL. Receptury
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: