Programmazione C++ moderna A Darla, straordinario esemplare nero di Labrador Retriever Scott Meyers Programmazione C++ moderna 42 modi per sfruttare al meglio le nuove funzionalità di C++11 e C++14 EDITORE ULRICO HOEPLI MILANO Titolo originale: Effective Modern C++ Copyright © 2015 Scott Meyers. All rights reserved. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same. Per l'edizione italiana Copyright © Ulrico Hoepli Editore S.p.A. 2015 via Hoepli 5, 20121 Milano (Italy) tel. +39 02 864871 – fax +39 02 8052886 e-mail [email protected] Seguici su Twitter: @Hoepli_1870 www.hoepli.it Tutti i diritti sono riservati a norma di legge e a norma delle convenzioni internazionali ISBN EBOOK 978-88-203-6982-8 Traduzione: Paolo Poli Progetto editoriale: Maurizio Vedovati – Servizi editoriali ([email protected]) Copertina: Sara Taglialegne Realizzazione digitale: Promedia, Torino Sommario Ringraziamenti Introduzione 1 Deduzione del tipo Elemento 1 – La deduzione del tipo nei template Caso 1 – ParamType è un riferimento o un puntatore, ma non un riferimento universale Caso 2 – ParamType è un riferimento universale Caso 3 – ParamType non è né un puntatore né un riferimento Argomenti array Argomenti funzione Elemento 2 – La deduzione del tipo auto Elemento 3 – Come funziona decltype Elemento 4 – Come visualizzare i tipi dedotti Editor IDE Diagnostiche di compilazione Output runtime 2 Il modificatore auto Elemento 5 – Preferire auto alle dichiarazioni esplicite del tipo Elemento 6 – Uso di un inizializzatore di tipo esplicito quando auto deduce tipi indesiderati 3 Passare al C++ moderno Elemento 7 – Distinguere fra () e {} nella creazione degli oggetti Elemento 8 – Preferire nullptr a 0 e NULL Elemento 9 – Preferire le dichiarazioni alias ai typedef Elemento 10 – Preferire gli enum con visibilità a quelli senza visibilità Elemento 11 – Preferire le funzioni “cancellate” a quelle private undefined Elemento 12 – Dichiarare con override le funzioni che lo richiedono Elemento 13 – Preferire i const_iterator agli iterator Elemento 14 – Dichiarare noexcept le funzioni che non emettono eccezioni Elemento 15 – Usare constexpr quando possibile Elemento 16 – Rendere sicure per i thread le funzioni membro const Elemento 17 – La generazione di funzioni membro speciali 4 I puntatori smart Elemento 18 – Uso di std::unique_ptr per la gestione delle risorse a proprietà esclusiva Elemento 19 – Usare std::shared_ptr per la gestione delle risorse a proprietà condivisa Elemento 20 – Usare std::weak_ptr per puntatori di tipo std::shared_ptr che possono “pendere” Elemento 21 – Usare std::make_unique e std::make_shared per gestire l’uso di new Elemento 22 – Idioma Pimpl: definire speciali funzioni membro nel file di implementazione 5 Riferimenti rvalue, semantica di spostamento e perfect-forward Elemento 23 – Parliamo di std::move e std::forward Elemento 24 – Distinguere i riferimenti universali e riferimenti rvalue Elemento 25 – Usare std::move sui riferimenti rvalue e std::forward sui riferimenti universali Elemento 26 – Evitare l’overloading sui riferimenti universali Elemento 27 – Alternative all’overloading per riferimenti universali Abbandonare l’overloading Passaggio per const T& Passaggio per valore L’approccio tag dispatch Vincolare i template che accettano riferimenti universali Compromessi Elemento 28 – Il collasso dei riferimenti Elemento 29 – Operazioni di spostamento: se non sono disponibili, economiche o utilizzate Elemento 30 – Casi problematici di perfect-forward Inizializzatori a graffe 0 o Null come puntatori nulli Dati membro static const interi solo nella dichiarazione Nomi di funzioni overloaded e nomi template Campi bit Conclusioni 6 Le espressioni lambda Elemento 31 – Evitare le modalità di cattura di default Elemento 32 – Usare la cattura iniziale per spostare gli oggetti nelle closure Elemento 33 – Usate decltype sui parametri auto&& per inoltrarli con std::forward Elemento 34 – Preferite le lambda a std::bind 7 L’API per la concorrenza Elemento 35 – Preferire la programmazione basata a task piuttosto che basata a thread Elemento 36 – Specificare std::launch::async quando è essenziale l’asincronicità Elemento 37 – Rendere gli std::thread non joinable su tutti i percorsi Elemento 38 – Attenzione al comportamento variabile del distruttore dell’handle del thread Elemento 39 – Considerate l’uso di future void per la comunicazione di eventi “one-shot” Elemento 40 – Usare std::atomic per la concorrenza e volatile per la memoria speciale 8 Tecniche varie Elemento 41 – Considerare il passaggio per valore per i parametri la cui copia è “poco costosa” e che pertanto vengono sempre copiati Elemento 42 – Impiegare l’emplacement al posto dell’inserimento Indice analitico Informazioni sul Libro Circa l’autore Ringraziamenti Ho iniziato a studiare ciò che allora era chiamato C++0x (il nascente C++11) nel 2009. Ho postato numerose domande sul newsgroup comp.std.c++ e sono grato ai membri di tale comunità (e in particolare a Daniel Krügler) per la grande utilità delle loro risposte. Negli anni più recenti, mi sono rivolto a “Stack Overflow” quando ho avuto domande sul C++11 e il C++14 e sono ugualmente in debito con tale comunità per l’aiuto ricevuto nella comprensione dei dettagli più reconditi della moderna programmazione C++. Nel 2010 ho preparato il materiale per un corso sul C++0x (pubblicati poi con il nome di Overview of the New C++, Artima Publishing, 2010). Questo materiale e la mia conoscenza si sono enormemente avvantaggiati dalla supervisione tecnica svolta da Stephan T. Lavavej, Bernhard Merkle, Stanley Friesen, Leor Zolman, Hendrik Schober e Anthony Williams. Senza il loro aiuto, probabilmente non sarei stato in grado di scrivere questo libro. Il suo titolo, a proposito, è stato suggerito o comunque sostenuto da numerosi lettori che hanno risposto al mio post Help me name my book del 18 febbraio 2014 sul mio blog e Andrei Alexandrescu (autore di Modern C++ Design, Addison-Wesley, 2001) ha avuto la gentilezza di benedire la scelta di quel titolo, sostenendo che non attingeva alla sua scelta terminologica. Mi è davvero impossibile risalire all’origine di tutte le informazioni contenute in questo libro, ma alcune fonti hanno avuto un impatto diretto. Nell’Elemento 4, l’uso di un template indefinito per estrarre le informazioni sul tipo da parte dei compilatori è stato suggerito da Stephan T. Lavavej, e Matt P. Dziubinski ha portato alla mia attenzione Boost. TypeIndex. Nell’Elemento 5, l’esempio unsigned-std::vector<int>::size_type deriva da un articolo del 28 febbraio