Questa è una lista degli operatori nei linguaggi di programmazioneC e C++. Tutti gli operatori seguenti sono implementabili in quest'ultimo linguaggio, mentre invece altri non lo sono in C, come nel caso degli operatori di conversione di tipo (casting), ossia const_cast, static_cast, dynamic_cast, e reinterpret_cast[1][2][3].
Molti degli operatori disponibili in C e C++ sono implementabili pure in altri linguaggi della cosiddetta “famiglia C”, quali C#, D, ma anche Java, Perl e PHP, mantenendo le medesime caratteristiche mostrate (arietà, posizione e associatività)[4][5].
Sintassi degli operatori
Nelle tabelle seguenti, a, b e c rappresentano valori validi qualsiasi (literals, valori da variabili, oppure return value) o, in alcuni casi specifici, nomi di oggetti o lvalues. R, S e T indicano qualsiasi tipo, mentre K indica un tipo di classe o un tipo enum[6][7].
Operatori aritmetici
Tutti gli operatori aritmetici esistono sia in C e C++ e possono essere, come già indicato, sovraccaricati solo in C++[8].
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso.
Decremento
Prefisso
--a
R& K::operator --();
R& operator --(K& a);
Postfisso
a--
R K::operator --(int);
R operator --(K& a, int);
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso.
Operatori relazionali
Tutti gli operatori di confronto possono essere sovraccaricati solo in C++. Dal C++20, l'operatore di disuguaglianza viene generato automaticamente se viene definito operator== e tutti e quattro gli operatori relazionali vengono generati automaticamente se viene definito operator<=>.
Nome operatore
Sintassi
Incluso in C
Esempi di implementazione in C++
Come membro della classe K
Fuori dalla classe
Uguale
a = b
Sì
bool K::operator ==(S const& b) const;
bool operator ==(K const& a, S const& b);
Diverso
a != b
Sì
bool K::operator !=(S const& b) const;
bool operator !=(K const& a, S const& b);
Maggiore di
a > b
Sì
bool K::operator >(S const& b) const;
bool operator >(K const& a, S const& b);
Minore di
a < b
Sì
bool K::operator <(S const& b) const;
bool operator <(K const& a, S const& b);
Maggiore o uguale a
a >= b
Sì
bool K::operator >=(S const& b) const;
bool operator >=(K const& a, S const& b);
Minore o uguale a
a <= b
Sì
bool K::operator <=(S const& b) const;
bool operator <=(K const& a, S const& b);
Operatori logici (o booleani)
Tutti gli operatori logici (o booleani[9]) sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricati solo in quest'ultimo linguaggio di programmazione.
Nonostante la possibilità di sovraccarico in C++, tale operazione è sconsigliata con AND logico e OR logico sia sconsigliato perché, come operatori sovraccaricati, si comporterebbero come normali chiamate di funzione, il che significa che entrambi i loro operandi verrebbero valutati, perdendo di conseguenza la loro importante valutazione di McCarthy. [10]
Nome dell'operatore
Sintassi
Esempi di implementazione in C++
Come membro della classe K
Fuori dalla classe
NOT logico
! a
bool K::operator !();
bool operator !(K a);
AND logico
a && b
bool K::operator &&(S b);
bool operator &&(K a, S b);
OR logico
a || b
bool K::operator ||(S b);
bool operator ||(K a, S b);
Operatori bit a bit
Tutti gli operatori bit a bit (o di manipolazione dei bit) esistono sia C che C++ e possono essere sovraccaricati soltanto nel linguaggio inventato da Bjarne Stroustrup[11].
Nome dell'operatore
Sintassi
Esempi di implementazione in C++
Come membro della classe K
Fuori dalla classe
Complemento a uno (inversione di tutti i bit)
~a
R K::operator ~();
R operator ~(K a);
AND bit a bit
a & b
R K::operator &(S b);
R operator &(K a, S b);
OR inclusivo bit a bit
a | b
R K::operator |(S b);
R operator |(K a, S b);
OR esclusivo bit a bit (OR exclusive, XOR)
a ^ b
R K::operator ^(S b);
R operator ^(K a, S b);
Spostamento di tutti i bit verso sinistra
a << b
R K::operator <<(S b);
R operator <<(K a, S b);
Spostamento di tutti i bit verso destra
a >> b
R K::operator >>(S b);
R operator >>(K a, S b);
Operatori ed espressioni di assegnamento
Tutti gli operatori e le espressioni di assegnamento sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricate solo in quest'ultimo linguaggio di programmazione.
Per gli operatori indicati, la semantica dell'espressione di assegnazione combinata incorporata a ⊚= b è equivalente a a = a ⊚ b, eccetto per il fatto che a viene valutata una sola volta.
C++ possiede molti operatori già predefiniti in grado di operare su vari tipi di dato, perciò si può parlare di “operatori già sovraccaricati”. Per esempio l’operatore + può essere implementato per sommare sia due variabili di tipo int (interi), sia due variabili di tipo float o double (variabili a virgola mobile).
Oltre a quando detto in precedenza, il programmatore può anche “estendere” l’operatività di ciascuno di essi; per esempio operandi complessi definiti con le classi, si potrebbero trattare anche come se fossero dati semplici[1].
In C++ è possibile sovraccaricare tutti gli operatori predefiniti, con l’eccezione su accesso al membro (.), indirezione di accesso al membro (.*) e risoluzione di ambito (::).
Nel caso degli operatori, il numero degli operandi e la priorità dell’operatore sovraccaricato sono le stesse dell’operatore predefinito, mentre il nuovo operatore può essere definito come una funzione membro oppure come una funzione friend (funzione amica)[14].
Proprietà degli operatori
Ogni operatore possiede delle proprietà, in modo tale da permettere l'interpretazione univoca del significato di ogni singola espressione. Gli esempi che seguono sono tutti applicabili al linguaggio C++ e, a seconda dei casi, anche al C[15].
Posizione
Un operatore può precedere gli operandi (o argomenti), seguirli oppure né precederli né seguirli[12]. In questi casi parleremo rispettivamente di operatori prefissi, postfissi oppure infissi.
Numero di operandi
Ogni operatore può avere un numero diverso di operandi (arietà).
a[b]
Nel caso riportato, l'operatore di indicizzazione possiede due operandi, ossia le parentesi quadre. L'arietà può essere anche di tre operandi, come nel caso degli operatori condizionali, riportato di seguito:
e1?e2:e3
Precedenza degli operatori
Ogni operatore possiede una propria precedenza[16][17], rappresentata attraverso un numero naturale tra 1 e 18 in ordine decrescente, dove più è piccolo il numero, maggiore sarà la priorità; per esempio un operatore con priorità 1 avrà maggiore priorità su un operatore con priorità 13.
Nell'esempio che segue è possibile osservare una semplice applicazione pratica delle precedenze:
#include<iostream>usingnamespacestd;intmain(){inta=5;intb=6;intc=8;intop=a+b*c;cout<<op<<endl;// Stampa a schermoreturn0;}
L'operatore moltiplicazione (*) possiede priorità 5, mentre l'operatore addizione (+) possiede priorità 6; pertanto il risultato a schermo sarà 53 e non 19.
Associatività degli operatori
L'associatività indica l'ordine in cui vengono eseguiti gli operatori aventi la stessa priorità tra loro[12]. Viene riportato un semplice esempio con gli operatori di divisione (priorità 5).
#include<iostream>usingnamespacestd;intmain(){floata=5.0;floatb=6.0;floatc=8.0;floatdiv=a/b/c;cout<<div<<endl;// Stampa a schermoreturn0;}
Nell'esempio precedente la stampa a schermo sarà 0.104167 perché l'operatore di divisione segue associatività a sinistra e, quindi, il calcolatore avrà eseguito la seguente equazione: .
Tabella delle precedenze e delle associatività
Nella tabella seguente sono elencati gli operatori di C e C++ in ordine di priorità con le loro rispettive associatività. Essi sono sempre implementabili su C++, mentre, quelli appositamente indicati nella penultima colonna da sinistra, non lo sono in C[12][17].
Priorità
Operatore
Nome
Implementabile in C
Associatività
1
(priorità massima)
class-name :: member
namespace-name :: member
Risolutore di visibilità
No
sinistra
:: member
Risolutore globale di visibilità
No
destra
2
object . member
Selezione
Sì
sinistra
pointer -> member
Deferenziazione e selezione
Sì
sinistra
array[ rvalue ]
Indicizzazione
Sì
sinistra
function ( actual-argument-list )
Chiamata di funzione
Sì
sinistra
lvalue ++
Postincremento
Sì
destra
lvalue --
Postdecremento
Sì
destra
static_cast type<rvalue>
Conversione di tipo
No
sinistra
const_cast type<rvalue>
Conversione const
No
sinistra
3
sizeof object
sizeof (type)
Dimensione
di oggettodi tipo
Sì
destra
++ lvalue
Preincremento
Sì
destra
-- lvalue
Predecremento
Sì
destra
~ rvalue
Complemento bit a bit
Sì
destra
! rvalue
Negazione
Sì
destra
- rvalue
Meno unitario
Sì
destra
+ rvalue
Più unitario
Sì
destra
& rvalue
Indirizzo
Sì
destra
* rvalue
Deferenziazione
Sì
destra
new type
Allocazione
No
destra
new type[]
Allocazione di array
No
destra
delete pointer
Deallocazione
No
destra
delete[] pointer
Deallocazione di array
No
destra
4
object .* pointer-to-member
Selezione con puntatore a membro
No
sinistra
pointer ->* pointer-to-member
Deferenziazione e selezione con puntatore a membro
No
sinistra
5
rvalue * rvalue
Moltiplicazione
Sì
sinistra
rvalue / rvalue
Divisione
Sì
sinistra
rvalue % rvalue
Modulo
Sì
sinistra
6
rvalue + rvalue
Addizione
Sì
sinistra
rvalue - rvalue
Sottrazione
Sì
sinistra
7
rvalue << rvalue
Traslazione sinistra
Sì
sinistra
rvalue >> rvalue
Traslazione destra
Sì
sinistra
8
rvalue < rvalue
Minore
Sì
sinistra
rvalue <= rvalue
Minore o uguale
Sì
sinistra
rvalue > rvalue
Maggiore
Sì
sinistra
rvalue >= rvalue
Maggiore o uguale
Sì
sinistra
9
rvalue == rvalue
Uguale
Sì
sinistra
rvalue != rvalue
Diverso
Sì
sinistra
10
rvalue & rvalue
AND bit a bit
Sì
sinistra
11
rvalue ^ rvalue
OR esclusivo bit a bit
Sì
sinistra
12
rvalue | rvalue
OR bit a bit
Sì
sinistra
13
rvalue && rvalue
AND logico
Sì
sinistra
14
rvalue || rvalue
OR logico
Sì
sinistra
15
co_await
co_yield
Coroutine
No
destra
16
rvalue ? rvalue : rvalue
Espressione condizionale
Sì
sinistra
17
lvalue = rvalue
Assegnamento
Sì
destra
lvalue += rvalue
Addizione e assegnamento
Sì
destra
lvalue -= rvalue
Sottrazione e assegnamento
Sì
destra
lvalue *= rvalue
Moltiplicazione e assegnamento
Sì
destra
lvalue /= rvalue
Divisione e assegnamento
Sì
destra
lvalue %= rvalue
Modulo e assegnamento
Sì
destra
lvalue &= rvalue
AND bit a bit e assegnamento
Sì
destra
lvalue |= rvalue
OR bit a bit e assegnamento
Sì
destra
lvalue ^= rvalue
OR esclusivo bit a bit e assegnamento
Sì
destra
lvalue <<= rvalue
Traslazione a sinistra e assegnamento
Sì
destra
lvalue =>> rvalue
Traslazione a destra e assegnamento
No
destra
18
(priorità minima)
expression , expression
Virgola
Sì
sinistra
Critiche sulle precedenze degli operatori
La precedenza degli operatori logici bitwise è stata al centro di numerose critiche[18][19], poiché concettualmente, & e | sono operatori aritmetici come * e +.
Per esempio, l'espressione a & b == 7 viene sintatticamente analizzata come a & (b == 7), mentre l'espressione a + b == 7 viene analizzata come (a + b) == 7. Ciò richiede l'uso delle parentesi più spesso di quanto sarebbe altrimenti necessario.
Storicamente, non esisteva una distinzione sintattica tra gli operatori bit a bit e quelli logici. Nei linguaggi BCPL, B e nelle prime versioni di C, gli operatori && e || non esistevano proprio; invece, & | aveva un significato diverso a seconda che venisse usato in un “contesto di valore di verità” (cioè quando ci si aspettava il ritorno di un valore booleano, come in if (a==b & c) {...} e si comportava come un operatore logico, ma in c = a & b si comportava invece come un operatore bit a bit).
La sintassi attuale è stata mantenuta soltanto per consentire la retrocompatibilità con le installazioni già esistenti.
Peraltro, in C++ (e nelle versioni successive di C) le operazioni di uguaglianza, con l'eccezione dell'operatore di confronto logico a tre, producono valori di tipo bool che sono concettualmente un singolo bit (1 o 0) e come tali non appartengono propriamente alle operazioni bit a bit.
Sinonimi in C++
C++ definisce una serie di keywords che possono essere implementate come sinonimi rispetto agli operatori[20]; tali definizioni non sono assolutamente implementabili nel linguaggio C.
Keyword
Operator
and
&&
and_eq
&=
bitand
&
bitor
|
compl
~
not
!
not_eq
!=
or
||
or_eq
|=
xor
^
xor_eq
^=
Questi possono essere utilizzati esattamente come sostituti dei rispettivi simboli di punteggiatura; ciò significa, per esempio, che le espressioni (a > 0 AND NOT flag) e (a > 0 && flag) avranno un significato assolutamente identico.
^abcd Andrea Domenici e Graziano Frosini, Introduzione alla programmazione ed elementi di strutture dati con il linguaggio C++, collana "Informatica", diretta da A. L. Frisiani, VIII edizione, Milano/Pisa, ISBN978-8846462022.
^Per le conversioni definite dall'utente, il tipo di ritorno corrisponde implicitamente e necessariamente al nome dell'operatore, a meno che il tipo non sia dedotto. (es. operatorauto(), operatordecltype(auto)() etc.).
Andrea Domenici, Graziano Frosini, Introduzione alla programmazione ed elementi di strutture dati con il linguaggio C++, Franco Angeli Editore, 2013, p. 480, ISBN978-88-464-6202-2.