Топ-100
Indietro

ⓘ C++11. Il C++11, conosciuto anche come C++0x, è uno standard per il linguaggio di programmazione C++ che ha sostituito la revisione del 2003. Questo standard co ..




C++11
                                     

ⓘ C++11

Il C++11, conosciuto anche come C++0x, è uno standard per il linguaggio di programmazione C++ che ha sostituito la revisione del 2003. Questo standard comprende numerose novità per il core del linguaggio ed estende la libreria standard incorporando molte delle librerie del cosiddetto TR1.

Il" C++ Standards Committee ” ha completato il nuovo standard nel settembre 2008 e la bozza è stata presentata con il nome di N3126 il 21 agosto 2010. Il 25 marzo 2011 lISO ha votato la bozza finale targata N3290 che è stata contrassegnata come FDIS Final Draft International Standard. Il 1º settembre 2011 è stata pubblicata la versione finale del C++11 da parte di ISO e IEC ISO/IEC 14882:2011E Programming Languages - C++, Third Edition) Molte software house e progetti open source stanno sviluppando vari compilatori già funzionanti.

Una delle ragioni che spingono ad un processo evolutivo un linguaggio di programmazione come il C++ è la necessità di poter programmare più velocemente, elegantemente e, soprattutto, ottenendo un codice la cui manutenzione sia agevole. Questo processo porta inevitabilmente verso lincompatibilità con il vecchio codice, per questo motivo, durante il processo di sviluppo del C++, ogni tanto si sono presentate alcune incompatibilità con le versioni precedenti. Secondo quanto annunciato da Bjarne Stroustrup inventore del linguaggio C++, nonché membro del comitato, questo standard ha mantenuto pressoché al 100% la compatibilità con lo standard precedente.

I maggiori benefici non arrivano da soluzioni che permetteranno di scrivere meglio una linea individuale di codice, ma da quelle soluzioni che consentono al programmatore di risolvere un problema ed organizzare meglio il codice; così come avvenuto con lintroduzione della programmazione orientata agli oggetti ed alla programmazione generica i template.

                                     

1. Le modifiche annunciate per limminente aggiornamento dello standard

Come detto le modifiche allo standard C++ riguarderanno sia il linguaggio di programmazione e sia la libreria standard.

Nello sviluppo di ogni utility del nuovo standard, il comitato ha applicato alcune direttive:

  • Rendere il C++ facile da insegnare e da apprendere a vantaggio dei principianti, senza rimuovere nessuna particolarità necessaria ai programmatori esperti.
  • Incrementare la sicurezza dei tipi fornendo soluzioni sicure alle correnti soluzioni insicure;
  • Mantenere stabilità e compatibilità con il C++98 e possibilmente anche con il C;
  • Rendere il C++ un linguaggio migliore per la programmazione di sistemi e librerie, piuttosto che introdurre nuove risorse dedicate soltanto ad applicazioni particolari;
  • Incrementare le performance le capacità di lavorare direttamente con lhardware;
  • Preferire lintroduzione di nuove risorse attraverso la libreria standard, anziché estendere il core del linguaggio;
  • Fornire soluzioni adatte al mondo reale;
  • Prediligere le modifiche che possono evolvere il modo di programmare;
  • Attuare il principio" zero-overhead” il supporto addizionale richiesto per alcune utility va impiegato solo se la utility è effettivamente utilizzata dal codice;

Lattenzione verso i principianti è molto importante, sia perché sono e saranno sempre la maggioranza rispetto agli esperti, sia perché molti principianti non intendono approfondire la loro conoscenza del C++, limitandosi ad operare nei campi in cui sono specializzati.

                                     

2. Estensioni al Linguaggio di programmazione C++

Le attenzioni migliori del comitato per il C++ sono rivolte allo sviluppo del core del linguaggio. È sullavanzamento dei lavori di questa parte dello standard che dipende la data di presentazione del C++0x.

Spesso si critica il C++ per la gestione non sicura dei tipi. Ma il C++, anche con il prossimo standard, non potrà diventare completamente sicuro nella gestione dei tipi di dato come il Java, perché, per fare un esempio, comporterebbe leliminazione dei puntatori non inizializzati e quindi snaturerebbe tutto il linguaggio. Nonostante ciò sono in molti ad insistere perché lo standard C++ preveda dei meccanismi per la gestione sicura dei puntatori. Per questo motivo il nuovo standard fornirà un supporto per gli smart pointer, ma solo attraverso la libreria standard.

Le aree dove il core del C++ sarà molto migliorato sono quelle per un miglior supporto al multithreading, alla programmazione generica, ed a meccanismi di costruzione ed inizializzazione più flessibili.

                                     

2.1. Estensioni al Linguaggio di programmazione C++ Utilità per il Multitasking

Il comitato del C++ ha intenzione di introdurre per il prossimo standard degli strumenti per la programmazione in multiprocessing e per la programmazione in multithreading.

Un supporto completo per il multiprocessing appare, per il momento, troppo dipendente dal sistema operativo utilizzato e troppo complesso, per essere risolto solo attraverso unestensione del linguaggio. Si ritiene che il supporto alla programmazione tra processi dovrebbe essere realizzato mediante una libreria di alto livello, da preferirsi rispetto ad una libreria di basso livello con primitive di sincronizzazione potenzialmente pericolose, poiché il comitato non intende incentivare il programmatore ad utilizzare una libreria standard potenzialmente pericolosa, invece di una libreria sicura anche se non standardizzata.

Nel prossimo standard verranno aggiunti al core del C++ alcune utilità per il multithreading, mentre lo sviluppo di una libreria per i thread resta un obiettivo meno prioritario per il comitato.



                                     

2.2. Estensioni al Linguaggio di programmazione C++ Esecuzione parallela

Sebbene la proposta vada ancora definita, è possibile che venga introdotto nel prossimo standard o in futuro un meccanismo per implementare il costrutto cobegin e coend, utilizzando i thread.

Per il momento non ha molta importanza quale keyword verrà utilizzata per introdurre la nuova notazione. È importante, invece, farsi unidea di come sarà facile, mediante poche linee di codice, implementare blocchi di istruzioni eseguibili in parallelo.

Tutti i vari blocchi vengono eseguiti in parallelo. Dopo che lesecuzione di ognuno è terminata, il programma riprende la sua esecuzione con un singolo thread.

                                     

2.3. Estensioni al Linguaggio di programmazione C++ Chiamata di funzione asincrona

Unaltra proposta ancora da definire, che potrebbe essere introdotta nel prossimo standard o in futuro, è un meccanismo, mediante thread, per effettuare una chiamata asincrona ad una funzione.

La sintassi probabilmente assomiglierà a quella dellesempio successivo:

                                     

2.4. Estensioni al Linguaggio di programmazione C++ Thread-Local Storage

Spesso in ambiente multithread è necessario possedere delle variabili uniche per ogni thread. Questo già avviene per le variabili locali delle funzioni, mentre non avviene per le variabili globali o statiche.

È stato proposto, per il prossimo standard, un nuovo" specificatore di classe di memorizzazione”, che si aggiunge ai numerosi già esistenti. Il nuovo specificatore dovrebbe chiamarsi semplicemente thread al momento nella documentazione ufficiale viene utilizzato __thread.

Gli oggetti thread sono simili agli oggetti globali. Mentre gli oggetti globali hanno un campo di esistenza che copre tutta lesecuzione del programma, gli oggetti thread hanno un campo di esistenza limitato ad un singolo thread, al cui termine la variabile non può essere più acceduta. Un oggetto thread può essere inizializzato come una qualsiasi variabile di durata statica, eventualmente impiegando un costruttore; se possiede un distruttore, questo verrà chiamato al termine del thread.



                                     

2.5. Estensioni al Linguaggio di programmazione C++ Operazioni atomiche

Nella multiprogrammazione non cè solo il problema della sincronizzazione tra i vari thread, spesso un thread ha la necessità di eseguire delle operazioni senza che nessuno lo interrompa, questo può avvenire ed esempio nei sistemi Real-Time quando bisogna operare con una periferica, oppure quando il thread necessita un accesso esclusivo a tutte le variabili globali.

Per eseguire senza interruzioni una serie di operazioni dette atomiche è stata proposta la nuova keyword atomic, utilizzabile come nellesempio seguente:

                                     

2.6. Estensioni al Linguaggio di programmazione C++ Un nuovo significato per il modificatore di accesso volatile

Nelle versioni di C++ precedenti allundicesima, il modificatore di accesso volatile informa il compilatore che il valore di una variabile può essere modificato in qualsiasi momento, indipendentemente dalla volontà del thread; per esempio se la variabile è un registro di un dispositivo memory mapped. Lutilizzo di volatile è importante perché limita le ottimizzazioni del compilatore, altrimenti esso assumerebbe che la variabile possa essere modificata solo nel lato sinistro di unistruzione di assegnamento.

È stato proposto di utilizzare la keyword volatile per indicare anche gli oggetti destinati alla comunicazione tra thread, o comunque condivisi tra più thread. In modo che la scrittura e la lettura di questi oggetti venga protetta automaticamente dal compilatore, mediante sincronizzazione, per evitare errori derivati da un accesso concomitante. Tuttavia questa soluzione al momento appare inappropriata se applicata ad oggetti complessi, ed è ancora in fase di discussione.

                                     

2.7. Estensioni al Linguaggio di programmazione C++ Utilità per le Classi

Lenfasi sulle caratteristiche generali delle classi del C++ è stato il motore principale per il successo di questo linguaggio di programmazione. Per questo è facile immaginare come le innovazioni più significative al core del C++ siano proprio dedicate allo sviluppo di nuove utilità rivolte ad incrementare le performance delle classi.

                                     

2.8. Estensioni al Linguaggio di programmazione C++ Operatori di conversione espliciti

Lutilizzo implicito degli operatori di conversione di tipo rappresenta una delle insidie più frequenti per un programmatore. Per questo motivo viene consigliato di utilizzare il più possibile operatori di conversione come lo static_cast.

Una possibile soluzione a questo problema sarebbe leliminazione del cast implicito da parte del compilatore come suggerito da alcuni puristi e magari anche del cast C-style, anchesso fonte di errori. Ma questa soluzione non è praticabile, perché comporterebbe problemi di compatibilità e renderebbe il linguaggio C++ troppo verboso. Per il momento si è pensato di introdurre gli" operatori di conversione espliciti ”, questi, a differenza degli operatori di conversione generici, possono essere invocati soltanto attraverso il cast C-style e lo static_cast. Lesempio successivo chiarisce la sintassi e lutilizzo della nuova utility.

                                     

2.9. Estensioni al Linguaggio di programmazione C++ Sequence constructors

Lidea di base è quella di permettere linizializzazione dei vettori di oggetti definiti dallutente, facile tanto quanto linizializzazione dei vettori di tipi predefiniti, come nellesempio:

Se invece abbiamo una classe simile alla seguente, nello standard C++98 non esiste la possibilità di inizializzare in modo così diretto e chiaro i suoi membri:

Lidea è quella di introdurre un costruttore speciale a partire da una sequenza di oggetti:

Linizializzatore di sequenza initializer_list è in realtà una classe template definita come segue:

Ecco una possibile definizione del costruttore di sequenza della classe vettore:

                                     

2.10. Estensioni al Linguaggio di programmazione C++ Costruttori delegati

Una classe può possedere molti costruttori e spesso questi debbono inizializzare allo stesso modo variabili e classi base ereditate. Capita altrettanto spesso che non sia possibile scrivere una funzione di inizializzazione, perché le classi base ed i riferimenti & vanno inizializzati prima di poter accedere al corpo del costruttore. Inoltre il riassegnamento è sconsigliabile per le classi ereditate e impossibile per i riferimenti. Questo porta il programmatore a dover ripetere più volte le stesse istruzioni per ogni costruttore, con risultati disastrosi sul fronte del mantenimento del codice.

È stato pertanto proposto che i costruttori possano richiamare altri costruttori, delegando a questi ultimi il compito di inizializzare loggetto:

Nellesempio si nota come il costruttore di copie ed il costruttore di default siano dei" Delegating Constructors”, perché affidano il compito della costruzione delloggetto al costruttore privato coordinata.



                                     

2.11. Estensioni al Linguaggio di programmazione C++ Forwarding Constructors

Capita spesso che derivando una classe si vogliano mantenere alcuni dei costruttori della classe base, nello standard C++98 questo è possibile soltanto ridefinendo tutti i costruttori utilizzati. La nuova proposta inserita nel core del linguaggio C++ è tanto semplice quanto utile.

In pratica si tratta solo di estendere lutilizzo di using, già utilizzabile per gli oggetti membro e per le funzioni membro, anche ai costruttori.

                                     

2.12. Estensioni al Linguaggio di programmazione C++ Asserzioni statiche

Le asserzioni sono espressioni booleane che esprimono una proprietà di un oggetto. Lo standard C++98 fornisce due possibilità per testare le asserzioni, la macro ASSERT e la direttiva di preprocessore #error. Però nessuna di queste è adatta per essere utilizzata nei template: la macro testa lasserzione a tempo di esecuzione, mentre la direttiva di preprocessore testa lasserzione a tempo di compilazione, ma prima che il template venga istanziato.

La nuova utility prevede lintroduzione di un nuovo metodo per testare le asserzioni a tempo di compilazione, mediante la nuova parola chiave static_assert. La dichiarazione assume la forma seguente:

Ecco alcuni esempi di come la static_assert possa essere utilizzata:

Quando lespressione costante viene valutata false il compilatore genera un messaggio di errore. Il primo esempio rappresenta unalternativa alla direttiva di preprocessore #error, nel secondo esempio invece lasserzione viene valutata per ogni istanziazione della classe template Check.

                                     

2.13. Estensioni al Linguaggio di programmazione C++ Espressioni Lambda

Le espressioni Lambda sono delle funzioni senza nome definite al momento della chiamata. Per comprendere il vantaggio di utilizzare le espressioni Lambda, immaginiamo una semplice funzione che scandisce un vettore, applicando ad ogni elemento unoperazione:

Volendo utilizzare la stessa funzione con tipi diversi possiamo trasformare la funzione in una funzione template:

Se invece volessimo che la funzione, a seconda delle necessità, eseguisse operazioni diverse, potremmo utilizzare una enumerazione:

Oppure se volessimo un codice più elegante potremmo utilizzare i puntatori a funzione:

Alcuni ritengono che queste soluzioni siano troppe verbose, allora si è pensato di introdurre le" espressioni Lambda ”. Se vogliamo resettare tutto il vettore scriveremo semplicemente:

Il segmento int & x{ x = 0; } è lespressione lambda che assegna 0 ad ogni elemento del vettore.

Unespressione lambda presenta la seguente sintassi:

  • Svolgimento della funzione. Il tipo del valore di ritorno è dedotto dallo svolgimento, o impostato a void se non vi è alcun return.
  • Sezione capture: fra le parentesi quadre è possibile specificare variabili o riferimenti a variabili esterne alla lambda di cui tener conto anche allinterno della stessa
  • Lista dei parametri che la funzione deve ricevere

Tornando allesempio precedente, è possibile eseguire anche altre operazioni per ogni elemento del vettore, ad esempio visualizzare gli elementi:

Le variabili catturate fra le quadre possono essere utilizzate allinterno delle espressioni Lambda. Nellesempio successivo si cattura un riferimento a n e si inizializza lelemento n-esimo al valore di n:

Unespressione lambda può avere un qualsiasi numero di argomenti. Ad esempio per ordinare un vettore in modo crescente potremo scrivere:

La versione presentata non è la versione definitiva delle funzioni lambda: quanto esposto copre solo in parte largomento delle espressioni lambda, ulteriormente migliorate nelle revisioni dello standard C++14 e C++17.

                                     

2.14. Estensioni al Linguaggio di programmazione C++ Deduzione del tipo: auto e decltype

Spesso la dichiarazione di una variabile non è molto agevole, soprattutto quando si tratta di tipi definiti allinterno di particolari template; prendiamo ad esempio la seguente dichiarazione:

Sebbene a volte sia necessario esplicitare per intero il tipo delloggetto che andiamo a creare, in questo caso probabilmente è superfluo, perché può essere ricavato dal tipo di ritorno della funzione.

Utilizzando la parola chiave auto informiamo il compilatore che il tipo della variabile sarà uguale a quello del secondo membro dellespressione:

Spesso è necessario associare il tipo di una variabile a quello di unaltra, tuttavia il programmatore è obbligato a dichiarare esplicitamente il tipo di ogni variabile che dichiara. Se il problema è leccessiva complessità della dichiarazione, allora conviene utilizzare il typedef, ma se lobiettivo è proprio quello di utilizzare lo stesso tipo di una variabile, qualunque esso sia, è possibile utilizzare la nuova parola chiave decltype:

Il comitato ritiene che il codice scritto utilizzando auto e decltype sia più ordinato e più mantenibile.

                                     

2.15. Estensioni al Linguaggio di programmazione C++ Utilità per i Template

Molti degli sforzi dei ricercatori sono puntati verso il miglioramento della programmazione generica, questo perché è diventata molto popolare e viene utilizzata per esprimere classi sempre più articolate, tanto da mettere in difficoltà i mezzi dellattuale C++. È necessario quindi che il codice generico possa esprimere soluzioni di maggiore complessità, questo senza sacrificare le caratteristiche che lo hanno reso tanto diffuso: i template sono infatti facili da scrivere, leggere ed impiegare.

                                     

2.16. Estensioni al Linguaggio di programmazione C++ Alias di template con using

Nelle versioni di C++ precedenti lundicesima è possibile utilizzare alias di template solo se tutta la lista dei parametri è definita mediante il typedef, né è possibile creare un alias se ci sono dei parametri indefiniti. Sebbene in alcuni casi sia possibile risolvere il problema utilizzando dei parametri di default, in altri lunica scorciatoia adottabile, che non risolve completamente il problema, è quella di creare un nuovo template che inglobi il template originario. Nellesempio che segue, infatti, i due tipi dichiarati dalle diverse tecniche di aliasing generica_ifc_1 e generica_ifc_2 non sono riconosciuti compatibili dal compilatore.

Il problema è importante perché laliasing permette un utilizzo più flessibile delle librerie template e quindi anche della STL Standard Template Library, ad esempio in situazioni come la seguente, sarebbe più comodo avere un alias in cui basti ripetere int una sola volta.

A partire dallo standard C++11 è prevista la possibilità di dichiarare alias di template utilizzando una nuova sintassi, che appunto consenta, per esempio, di scrivere:

Come già permesso per i typedef, questa nuova sintassi potrà essere utilizzata anche allinterno di una classe per creare un alias membro che quindi potrà essere public, protected o private.

                                     

2.17. Estensioni al Linguaggio di programmazione C++ Template variadici

Con il nuovo standard sarà introdotta la possibilità dichiarare template con numero arbitrario di parametri. Limpatto più importante sarà senza dubbio quello di poter estendere tutti i controlli, normalmente applicati ai parametri delle normali funzioni durante la compilazione, anche a quelle funzioni con numero arbitrario di parametri.

Per indicare lelenco dei parametri di lunghezza variabile si utilizza loperatore". ” che può eventualmente essere seguito dallidentificatore della lista dei parametri:

Sullidentificatore della lista dei parametri può essere applicata una sola operazione di disassemblaggio attraverso loperatore".”:

Nel penultimo esempio si è utilizzato la printf come esempio di variadic function, ma possono essere utilizzate anche funzioni con una normale lista di parametri, in questo caso il compilatore utilizza loverload di my_print corrispondente allelenco dei parametri inseriti.

Il compilatore, nellultimo esempio, espanderà i parametri Secondi allinterno della istanziazione della classe generica, se non esiste un overload del template che accetti due parametri, il codice è malformato e la compilazione da esito negativo. Da notare che il compilatore cercherà di istanziare generica anche se lelenco dei parametri è vuoto, e quindi cercherà un overload del template generica senza parametri.

Di seguito, un esempio di template utilizzabile per calcolare il tempo di esecuzione di qualunque funzione che non restituisca void:

                                     

2.18. Estensioni al Linguaggio di programmazione C++ Concept

Spesso i parametri che utilizziamo per un template devono possedere delle determinate caratteristiche, altrimenti rischiamo errori di compilazione, oppure questo è grave istanziazioni prive di logica. Nello standard attuale la definizione è lunico controllo che assumiamo sui parametri di un template, mentre sarebbe più opportuno controllare le sue istanziazioni indipendentemente dalla definizione del template, e viceversa. Per questo fu proposta lintroduzione dei concept: un sistema per migliorare la diagnostica degli errori quindi le prestazioni offerte dai template, senza perdere capacità espressiva. Tuttavia il gruppo ISO responsabile dello sviluppo dello standard decise di non introdurre i concept poiché ritenuti ancora immaturi rispetto alle scadenze che il gruppo stesso si era prefissato per la standardizzazione della nuova versione del linguaggio; è tuttavia probabile che tale funzionalità verrà proposta per i lavori dello standard successivo.

Osserviamo la funzione template seguente che esegue un ordinamento mediante lalgoritmo di quick sort:

È evidente che i tipi PUNT e result_of utilizzati in questa funzione devono possedere delle funzioni membro senza le quali la compilazione sarebbe impossibile.

La definizione di un Concept è composta da una lista di parametri, esplicitati come per la dichiarazione di un template, ed il corpo del Concept, una sequenza di semplici dichiarazioni che dipendono dai parametri della dichiarazione.

Il Concept viene introdotto nella definizione del template mediante la keyword where:

Con la stessa sintassi si può utilizzare la where anche allinterno della definizione di un concept per esprimere ulteriori restrizioni sulla combinazione di parametri.

Infine, allinterno della definizione della classe template, sarà possibile utilizzare i type traits una nuova utility della libreria standard, vedi il paragrafo relativo per pilotare la compilazione in funzione delle caratteristiche dei parametri assegnati.

                                     

2.19. Estensioni al Linguaggio di programmazione C++ Parentesi angolari

Con lintroduzione della programmazione generica attraverso i template fu necessario introdurre un nuovo tipo di parentesi. Oltre alle parentesi tonde, quadre e graffe, sono state introdotte le parentesi angolari. Il compilatore, dal momento in cui sono state introdotte le nuove parentesi, deve discriminare quando i caratteri < e > sono utilizzati nelle espressioni logiche, quando invece sono utilizzati come operatori di inserimento < < ed estrazione > >, oppure, per lappunto, quando sono utilizzati come parentesi angolari; questo ha fatto nascere ovviamente alcune ambiguità:

Lultimo esempio è un po astruso ma verificabile. Sono possibili due istanziazioni della classe X: una con I==false ed una con I==true ; il codice definisce unistanza di X in base al valore della relazione costante 1> 2 uguale a false ovviamente, ma il compilatore interpreta > come una parentesi angolare destra ed istanzia la X, quindi il resto del codice diventa privo di senso e la compilazione fallisce.

La soluzione al problema è abbastanza semplice: nel prossimo standard il compilatore, dopo lapertura di una parentesi angolare sinistra, dovrà interpretare la sequenza di caratteri > > come una doppia parentesi angolare destra, senza lasciarsi trarre in inganno dalle sequenze > = e > > =. Mentre per lultimo esempio il programmatore dovrà delimitare dalle parentesi tonde il contenuto della relazione costante.

In questo modo, dopo la parentesi tonda sinistra e fino alla parentesi tonda destra, il compilatore non riconosce più i caratteri come parentesi angolari.

                                     

2.20. Estensioni al Linguaggio di programmazione C++ typedef opaco

Questa caratteristica è stata giudicata come non pronta per il C++09, ma da poter riproporre nel futuro dal comitato.

Capita spesso nei programmi di definire più variabili dello stesso tipo, ma che rappresentano grandezze assolutamente non compatibili tra loro. Lesempio più comune è quello di un sistema di coordinate espresso tramite valori double:

In programmi di una certa complessità si avverte la necessità di apportare una distinzione netta tra i tre tipi, soprattutto per ragioni di chiarezza e di mantenibilità del codice:

Questa soluzione in apparenza sembra migliore della precedente ma, in pratica, il compilatore non effettua ancora nessun controllo sui parametri, i quali restano mutuamente sostituibili.

Lunica soluzione possibile è quella di definire tre nuove classi, e programmare tutti gli overload di ogni operatore ammesso. Questa soluzione è ideale, soprattutto se applicata in un programma di grande complessità, perché permette di ottenere un controllo totale sui nuovi tipi; lo sforzo per lo sviluppo dei nuovi oggetti sarebbe comunque poca cosa rispetto ai benefici ottenuti.

Tuttavia se il programmatore non necessita di un controllo così stringente, sarebbe molto più semplice introdurre un nuovo typedef che non crei un semplice alias, ma un nuovo oggetto. Per questo motivo è in fase di sviluppo il cosiddetto" typedef opaco”, mediante il quale sarà possibile creare un nuovo tipo che riceve in eredità tutte le caratteristiche del precedente, senza esserne il sostituto.

Saranno introdotti due tipi di typedef opachi: il typedef public ed il typedef private. Es:

Il primo permetterà ancora la conversione da origine a nuovo ovviamente non implicita, altrimenti saremmo ancora al punto di partenza e permetterà ancora una conversione implicita da nuovo a origine, però il nuovo tipo sarà ben distinto dal precedente.

Il secondo è la forma più restrittiva del concetto opaco: per il nuovo tipo, se si vuole permettere delle conversioni con il tipo originario e viceversa, bisognerà definire delle funzioni ad hoc.

La tecnica dei" typedef opachi” non è un metodo per utilizzare qualsiasi classe come se fosse una classe template. I nuovi tipi definiti in questa maniera restano degli alias degli originali seppur con qualche restrizioni.

                                     

2.21. Estensioni al Linguaggio di programmazione C++ long int

Fin dai tempi del C cè sempre stato un tipo integrale di troppo nel Core del linguaggio, in genere int era composto dallo stesso numero di byte della macchina sistema. Lo standard prevedeva per l int 16 o 32 bit condannando lo short int abbreviato short oppure il long int abbreviato long ad un ruolo subalterno.

Nei nuovi sistemi a 64 bit i produttori di compilatori hanno messo fine a questa ridondanza di definizione assegnando definitivamente:

  • 16 bit -> short int
  • 32 bit -> int
  • 64 bit -> long int

Tuttavia, nei sistemi a 32 bit, resta radicata labitudine dei produttori di compilatori ad utilizzare long int come numero a 64 bit. Il comitato del C++ si è sempre dimostrato riluttante a standardizzare nuovi tipi fondamentali che non siano anche stati adottati dal comitato del C che gode di assoluta indipendenza da quello del C++. Ora che la dicitura è diventata uno" standard di fatto”, questo vincolo sembra possa essere finalmente superato. Il comitato C++ ha approvato long int tra i tipi fondamentali compreso unsigned long int.

Daltra parte, in futuro, questa dicitura potrebbe essere ancora utilizzata in sistemi basati su processori con registri a 16 bit per indicare, appunto, numeri a 128 bit.

                                     

2.22. Estensioni al Linguaggio di programmazione C++ Puntatore nullo

Nello standard pre-11 allo" 0” spettava il doppio ruolo di costante intera e di puntatore nullo soluzione adottata fin dagli albori del C nel 1972.

Per anni i programmatori hanno risposto a questa possibile ambiguità utilizzando la costante" NULL”, al posto dello" 0”, anche per rendere il codice più comprensibile. Dal 2011 è stata inclusa una nuova parola chiave nullptr riservata esclusivamente per indicare il puntatore nullo.

Il nullptr non può essere assegnato ad un tipo intero, né confrontato con esso, mentre può essere confrontato ed assegnato a qualsiasi puntatore

Resta ovviamente possibile assegnare ad un puntatore la costante" 0” per ragioni di compatibilità, tuttavia lutilizzo di 0 e NULL è sconsigliato in ogni codice che non richieda retrocompatibilità, poiché potrebbe portare ad errori. Ad esempio, consideriamo il codice seguente:

                                     

2.23. Estensioni al Linguaggio di programmazione C++ La" enum class”

Nello standard C++-98 le enum non sono tipizzate come una classe, inoltre il tipo degli elementi è int e non se ne possono utilizzare altri. Possono essere utilizzati senza dichiararne lo scope e quindi non è possibile dichiarare in due distinte enumerazioni, due elementi con lo stesso nome. Inoltre gli elementi di una enumerazione possono essere convertiti implicitamente ad int e questo è da sempre causa di innumerevoli errori per il programma.

Se il programmatore vuole utilizzare dei tipi più sicuri, è obbligato a sviluppare delle classi come la seguente:

Le uniche azioni permesse tra oggetti di tipo valore sono lassegnamento ed il confronto, questo dovrebbe mettere al riparo da possibili errori, a patto di convertire ogni valore della enum costanti utilizzato allinterno del codice con un oggetto valore. Ad essere pignoli questa classe si presta facilmente ad essere trasformata in un template, per essere istanziata a partire da diverse enumerazioni.

A partire dallo standard C++11 non sono più necessarie dichiarazioni verbose come quella precedente per utilizzare una semplice enum ed avere lappoggio del compilatore per la ricerca di possibili errori sintattici. Sono infatti state introdotte due espressioni riguardanti gli enum:

  • Per gli enum classici si potrà indicare esplicitamente il tipo degli elementi della enumerazione. Il tipo dovrà essere un "integral type" intero o carattere, con o senza segno.

Resta invariata la sintassi per indicare gli elementi della enum.

  • Inoltre è stato introdotto un nuovo tipo di enumerazione: la enum class, fortemente tipizzata: non sono supportati i cast impliciti e, per indicare ogni elemento, è sempre necessario indicarne lo scope. Anche per la enum è possibile indicare esplicitamente il tipo degli elementi; se omesso sarà utilizzato int.
                                     

2.24. Estensioni al Linguaggio di programmazione C++ Ranged-for

A partire dal C++11è stato introdotto il ranged for, un metodo più veloce per iterare attraverso gli elementi di un array o di un container. Affinché un oggetto sia iterabile mediante un range-based for è necessario che disponga di un metodo begin e un metodo end, o che esista un overload di std:begin e std end per la classe delloggetto.

La sintassi di un range-based for è for tipo nome_elemento: nome_container { espressioni. } ; una caratteristica molto comoda di questo ciclo è che lelemento dichiarato è di tipo *container iterator, pertanto è possibile lavorare direttamente sullelemento invece che sulliteratore.

                                     

3. Estensioni alla libreria standard C++

Dalla Standard library del C++0x arriveranno le novità più ardite, anche se, in realtà, quasi tutte le nuove librerie non necessitano dellaggiornamento del core e potrebbero funzionare anche sullo standard C++ corrente.

La maggior parte delle librerie che saranno introdotte sono definite nel documento" Technical Report on C++ Library Extensions ” chiamato anche TR1, la cui stesura definitiva risale al 2005. Queste librerie sono già state adottate da alcuni compilatori e possono essere richiamate mediante il" namespace std tr1 ”.

È in preparazione un secondo technical report TR2 ma sicuramente verrà completato dopo la standardizzazione del C++0x. Per questa ragione il paragrafo corrente referenzia esclusivamente alcune delle librerie più significative introdotte attraverso il TR1.

                                     

3.1. Estensioni alla libreria standard C++ Tuple

Le tuple sono collezioni di dimensioni prestabilite composte da oggetti di tipo eterogeneo. Gli elementi di una tupla possono essere un qualsiasi tipo di oggetto.

Questa utilità viene implementata nellheader e beneficia di alcune estensioni del linguaggio C++, come:

  • riferimenti a riferimenti,
  • argomenti di default per le funzioni template disponibili solo per le classi.
  • template con lista di argomenti di lunghezza variabile,

Ecco la definizione di tupla nellheader:

Esempio di definizione ed uso di una tupla:

È possibile creare la tupla prova senza definirne il suo contenuto, ma questo solo se tutti gli oggetti della tupla possiedono il costruttore di default; inoltre si può assegnare una tupla ad unaltra: se le tuple sono dello stesso tipo, sarà necessario che tutti gli oggetti possiedano il costruttore di copie; se gli oggetti non corrispondono, sarà necessario che quelli del 2º membro siano convertibili a quelli del 1º, oppure che i tipi del 1º membro abbiano un costruttore adeguato:

Sono anche disponibili gli operatori di confronto tra tuple con uguale numero di elementi e fra tuple da due elementi e std pair ed inoltre sono disponibili due espressioni per controllare le caratteristiche delle tuple solo a tempo di compilazione:

  • tuple_size value Ritorna il valore del numero di elementi della tupla di tipo T.
  • tuple_element type Ritorna il tipo dellelemento numero I della tupla di tipo T.
                                     

3.2. Estensioni alla libreria standard C++ Tabelle di Hash

Linserimento nella libreria standard del C++ delle tabelle di Hash i contenitori associativi non ordinati è stata una delle richieste più frequenti. Sebbene queste soluzioni diano rendimenti inferiori rispetto agli alberi bilanciati se utilizzate nel caso peggiore ossia in presenza di molte collisioni, le loro performance sono migliori in molte applicazioni reali.

La gestione delle collisioni viene amministrata soltanto mediante linear chaining. Questo perché il comitato non ha ritenuto opportuno standardizzare soluzioni di open addressing che presentano parecchi problemi intrinseci soprattutto quando è ammessa la cancellazione di elementi. A causa delle possibili omonimie con librerie non standard, che nel frattempo hanno colmato la mancanza di una libreria standard per le tabelle di Hash, è stato utilizzato il prefisso" unordered” invece di" hash”.

La nuova utility prevede 4 tipi di tabelle di Hash, differenti a seconda che accettino no più elementi con la stessa chiave chiavi equivalenti o chiavi uniche ed a seconda che associno no un valore arbitrario alla chiave.

Le nuove classi sono modellate sul concetto di container, pertanto ne implementano tutte le funzioni, comprese quelle necessarie per accedere agli elementi come: insert, erase, begin, end.

Per utilizzare le tabelle di hash è necessario includere gli headers e secondo necessità.

                                     

3.3. Estensioni alla libreria standard C++ Espressioni regolari

A partire dal C++11 la libreria standard ha incluso una propria libreria per le gestione delle espressioni regolari. La nuova libreria, definita nel nuovo header, consiste in una coppia di nuove classi:

  • le corrispondenze sono rappresentate dalle istanze della classe template match_results.
  • le espressioni regolari sono rappresentate dalle istanze della classe template basic_regex ;

Per la ricerca si utilizza la funzione regex_search, mentre per la ricerca e la sostituzione si utilizza regex_replace, che fornisce una nuova stringa corretta come risultato. Gli algoritmi regex_search e regex_replace ricevono come input una espressione regolare ed una stringa di caratteri e scrivono le corrispondenze trovate nella struttura match_results.

Esempio di utilizzo di match_results:

La libreria" regex ” non richiede lalterazione di nessun header esistente e nessuna estensione del linguaggio.

                                     

3.4. Estensioni alla libreria standard C++ Puntatori smart per usi generici

La gestione dellallocazione dinamica della memoria è sempre stata, fin dai primi computer, un punto delicato della programmazione. Molti moderni linguaggi di programmazione tipo il Java offrono strumenti per la gestione automatica della memoria.

I puntatori ordinari del C++ hanno molte interessanti proprietà:

  • assegnarli,
  • convertirli ad una delle classi base attraverso un cast statico,
  • è possibile copiarli,
  • utilizzare il void * come puntatore generico,
  • utilizzarne il loro valore,
  • convertirli ad una delle classi derivate attraverso un cast dinamico.

Mentre i principali difetti dei puntatori ordinari del C++ sono:

  • la gestione manuale obbligata per gli oggetti allocati dinamicamente,
  • possono riferirsi ad un indirizzo non valido o non allocato della memoria.

I nuovi smart pointer mantengono le caratteristiche di forza dei puntatori ordinari eliminando le loro debolezze.

Utilizzando i puntatori shared_ptr la proprietà delloggetto viene ripartita egualmente a tutte le copie, allultima istanza rimasta viene delegata la responsabilità di distruggere loggetto. Per conoscere il numero di puntatori che fanno riferimento allo stesso oggetto è possibile utilizzare la use_count, una funzione membro di shared_ptr. La funzione membro reset permette di cancellare lo smart pointer. Un puntatore resettato si dice vuoto, quindi la sua funzione use_count ritorna sempre zero. Il puntatore weak_ptr non incide sul ciclo di vita delloggetto puntato, questo significa che in ogni momento è possibile che il weak_ptr venga invalidato. In questo modo è permesso a qualsiasi funzione o classe di mantenere un riferimento ad un oggetto senza influenzarne il ciclo di vita, a discapito però di una maggiore difficoltà di implementazione del codice.

Esempio di utilizzo dello shared_ptr:

Un terzo smart pointer è lo unique_ptr: un puntatore tale da esser lunico detentore dellarea di memoria che ha allocato. Non è possibile copiare uno unique_ptr: è solo possibile trasferire la proprietà da un puntatore allaltro mediante il supporto della funzione std move.

È possibile utilizzare gli smart pointers includendo lheader ; è inoltre buona norma rimpiazzare luso di auto_ptr, deprecato in questo sin da questo standard con i nuovi smart pointers.

                                     

3.5. Estensioni alla libreria standard C++ Utilità estensibile per numeri casuali

I computer hanno per definizione comportamento deterministico, tuttavia alcune applicazioni richiedono un comportamento non deterministico anche se solo in apparenza veicolato dalla generazione di numeri casuali.

La sola utilità standard presente prima del 2011 era la funzione rand, ma non è ben definita e la sua implementazione è delegata interamente ai produttori dei compilatori. Pertanto sono state introdotte nuove utilità per generare numeri casuali nellheader.

I generatori di numeri casuali sono costituiti da uno stato interno, ed una funzione che elabora il risultato e porta il generatore allo stato successivo. Queste due caratteristiche costituiscono il motore del generatore. Unaltra fondamentale caratteristica è infine la distribuzione dei risultati, ossia lintervallo e la densità della variabile aleatoria.

Attraverso il template variate_generator è possibile creare un generatore di numeri casuali selezionando il motore e la distribuzione desiderata. Si può scegliere tra i motori le distribuzioni fornite dallo standard, oppure utilizzare mezzi propri.

  • Motori per numeri pseudocasuali

Nella nuova libreria verranno introdotti alcuni motori per la generazione di numeri pseudocasuali. Questi sono tutti dei template, in questo modo lutente può personalizzarli come preferisce. Lo stato interno dei motori pseudocasuali è determinato attraverso un seme generalmente un insieme di variabili. Lapparente casualità è dovuta solo dalla limitata percezione dellutente.

* Moltiplicare il valore per la dimensione in byte del tipo utilizzato.

Le prestazioni di questi motori possono essere incrementate utilizzando la classe template discard_block, oppure possono essere combinate utilizzando la classe template xor_combine. Per comodità nellheader sono definite anche alcune istanze standard di motori; un esempio è la classe mt19937 istanziata su base mersenne_twister:

  • Motore per numeri non deterministici

Attraverso la classe random_device è possibile generare numeri non deterministici di tipo unsigned int. La sua implementazione richiederà lutilizzo di un dispositivo il cui stato sia indipendente dal sistema che ospita lapplicazione ad esempio da un contatore esterno non sincronizzato, oppure un trasduttore particolare e richiederà anche limpiego di un tradizionale motore pseudocasuale per, come si usa dire," temprare il risultato”.

  • Distribuzioni dei numeri casuali

La libreria definisce parecchi tipi di distribuzioni, dalle distribuzioni uniformi a quelle definite dalla teoria della probabilità: uniform_int, bernoulli_distribution, geometric_distribution, poisson_distribution, binomial_distribution, uniform_real, exponential_distribution, normal_distribution e gamma_distribution. Ovviamente lutente è libero di instanziare come preferisce le distribuzioni standard oppure di utilizzare una sua distribuzione compatibile.

Ecco un semplice esempio di implementazione:

                                     

3.6. Estensioni alla libreria standard C++ Funzioni matematiche speciali

Lheader definisce alcune funzioni matematiche abbastanza comuni:

  • potenze: pow, sqrt, cbrt, hypot ;
  • speciali: erf, erfc, tgamma, lgamma.
  • esponenziali: exp, exp2, frexp, ldexp, expm1 ;
  • iperboliche: sinh, cosh, tanh, asinh, acosh, atanh ;
  • trigonometriche: sin, cos, tan, asin, acos, atan e atan2 ;
  • logaritmiche: log10, log2, logb, ilogb, log1p ;

La proposta era di aggiungere nuove funzioni alla categoria speciali per colmare parecchie lacune che costringono ad utilizzare librerie non standardizzate, tuttavia tale cambiamento non è stato approvato per la versione finale del C++11. Chiaramente lutilizzo di queste funzioni sarebbe stato limitato allambito ingegneristico ed alle discipline scientifiche.

La tabella seguente riporta le 23 funzioni approvate per lo standard C++11.

Ad ogni funzione è sufficiente aggiungere il suffisso f per ottenere la versione float ed il suffisso l per la versione long double. Es:

                                     

3.7. Estensioni alla libreria standard C++ Wrapper reference

Il wrapper reference viene ottenuto da unistanza della classe template reference_wrapper nellheader, il suo utilizzo è simile al riferimento & previsto dal linguaggio C++. Per ottenere un wrapper reference da un oggetto qualsiasi si utilizza la funzione template ref per un riferimento costante si usa cref.

Il wrapper reference è utile soprattutto per le funzioni template quando vogliamo che ottengano un riferimento ai loro parametri invece di utilizzarne una copia:

                                     

3.8. Estensioni alla libreria standard C++ Wrapper polimorfi per oggetti funzione

I wrapper polimorfi per oggetti funzione Polymorphic Function Object Wrapper sono simili ai puntatori a funzione per semantica e sintassi, ma il loro utilizzo è meno vincolato e possono riferirsi indistintamente a qualsiasi funzione che possa essere chiama con argomenti compatibili con quelli del wrapper.

Attraverso lesempio seguente è possibile comprenderne le caratteristiche:

La classe template function è definita allinterno dellheader.

                                     

3.9. Estensioni alla libreria standard C++ I type traits per la metaprogrammazione

La metaprogrammazione consiste nel creare un programma che crei o modifichi un altro programma o se stesso. Questo può avvenire a tempo di compilazione oppure a tempo di esecuzione. Il comitato del C++ ha deciso di introdurre una libreria che consenta la metaprogrammazione a tempo di compilazione attraverso i template.

Ecco un ottimo esempio di quello che si può già ottenere, con lo standard attuale, attraverso la metaprogrammazione: una ricorsione di istanziazioni di template per il calcolo di una potenza.

Molti algoritmi possono essere utilizzati indistintamente per diversi tipi di dati, per questo motivo sono stati inseriti nello standard C++ i template, in modo da supportare la programmazione generica e rendere più compatto e gestibile il codice. Tuttavia capita spesso di imbattersi in algoritmi che necessitano di informazioni sui dati utilizzati. Queste informazioni possono essere ricavate durante listanziazione di una qualsiasi classe template utilizzando i type traits.

I type traits sono moltissimi e possono identificare la categoria di un oggetto e tutte le caratteristiche di una classe o di una struttura. Sono definiti nel nuovo header.

Nellesempio seguente cè la funzione template elabora che a seconda del tipo di dati inseriti istanzierà uno dei due algoritmi proposti funzione.do_it.

Attraverso i type traits, definiti nellheader, è possibile effettuare anche delle operazioni di trasformazioni sui tipi lo static_cast ed il const_cast non sono sufficienti allinterno di un template.

Questo tipo di programmazione permette di ottenere un codice elegante e conciso; il vero punto debole di queste tecniche è il debugging: disagevole a tempo di compilazione ed molto difficile durante lesecuzione del programma.

                                     

3.10. Estensioni alla libreria standard C++ Metodo Uniforme per determinare i tipi di ritorno di un oggetto funzione

Determinare a tempo di compilazione il tipo di ritorno di una funzione oggetto template, soprattutto se dipende dai parametri della funzione stessa, non è sempre intuitivo.

Prendiamo in esempio il codice seguente:

Istanziando la classe template calcolo, utilizzando come parametro la classe chiara ossia istanziando calcolo, la funzione oggetto di calcolo avrà sempre lo stesso tipo di ritorno di quello della funzione oggetto di chiara.

Se invece istanziamo la classe calcolo utilizzando la classe confusa ossia istanziando calcolo:

Il tipo di ritorno della funzione oggetto di calcolo non sarà lo stesso di quello della classe confusa potrà esserci una conversione da int a double o viceversa, a seconda dellistanziazione di calculus.operator.

La nuova libreria, proposta per il prossimo standard, introduce la classe template result_of, che permette al programmatore di determinare ed utilizzare il tipo di ritorno di una funzione oggetto per qualsiasi dichiarazione. Nella versione corretta calcolo_ver2 viene impiegata la nuova utility per ricavare il tipo di ritorno della funzione oggetto:

In questo modo nelle istanziazioni della funzione oggetto di calcolo_ver2 non ci saranno più conversioni.

Il problema di determinare il tipo di ritorno di una chiamata ad un oggetto funzione è un sottoinsieme del problema più generale di determinare il tipo di risultato di unespressione. Questo problema potrebbe essere risolto in futuro espandendo le funzionalità della typeof a tutte le casistiche possibili.