Most vexing parse
Il most vexing parse è una forma di soluzione di ambiguità sintattica del C++, definita nella sezione 8.2 dello standard[1], determinata dal fatto che la sintassi per l'inizializzazione di una variabile è in alcuni casi ambigua con quella di dichiarazione di una funzione. La locuzione most vexing parse è stata introdotta da Scott Meyers in Effective STL (2001).[2]
Esempio con classi e oggetti
modificaIl seguente è un esempio di dichiarazione o definizione ambigua:
class Timer {
public:
Timer();
};
class TimeKeeper {
public:
TimeKeeper(const Timer& t);
int get_time();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
La riga
TimeKeeper time_keeper(Timer());
potrebbe essere interpretata come
- la definizione di una variabile
time_keeper
di classeTimeKeeper
, inizializzata con un'istanza anonima della classeTimer
; - la dichiarazione di una funzione
time_keeper
con tipo di ritornoTimeKeeper
e un singolo parametro (senza nome) di tipo puntatore a funzione senza parametri e con tipo di ritornoTimer
.
Lo standard richiede di interpretare questa riga nella seconda maniera, per cui la riga successiva non sarà valida. Ad esempio, g++ restituisce il seguente messaggio di errore:
$ g++ -c time_keeper.cc
time_keeper.cc: In function ‘int main()’:
time_keeper.cc:15: error: request for member ‘get_time’ in ‘time_keeper’, which is
of non-class type ‘TimeKeeper(Timer (*)())’
in quanto time_keeper
è una funzione dichiarata alla riga precedente, per cui non è possibile chiamare su essa il metodo get_time()
.
Clang++ fornisce un messaggio di warning:
$ clang++ time_keeper.cc timekeeper.cc:14:25: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse] TimeKeeper time_keeper(Timer()); ^~~~~~~~~ timekeeper.cc:14:26: note: add a pair of parentheses to declare a variable TimeKeeper time_keeper(Timer()); ^ ( ) timekeeper.cc:15:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a structure or union return time_keeper.get_time(); ~~~~~~~~~~~^~~~~~~~~
Per fare in modo che l'istruzione venga interpretata come definizione di variabile con inizializzazione, è possibile aggiungere una coppia di parentesi supplementare:
TimeKeeper time_keeper( (Timer()) );
Esempio con cast funzionale
modificaUn altro esempio coinvolge l'uso del cast funzionale, quando viene usato per convertire il valore di un'espressione passata poi come variabile o come parametro di un costruttore
void f(double adouble) {
int i(int(adouble));
}
In questo caso, i
viene interpretata come una definizione di funzione, equivalente alla seguente
// takes an integer and returns an integer
int i(int adouble);
Per disambiguare l'espressione affinché venga interpretata come dichiarazione di variabile, si può usare la stessa tecnica dell'esempio precedente, oppure si può sostituire il cast funzionale con un cast in stile C
// declares a variable called 'i'
int i((int) adouble);
oppure si può usare l'opportuno operatore di casting del C++
// declares a variable called 'i'
int i(static_cast<int>(adouble));
Uniform initialization syntax
modificaLo standard C++11 ha introdotto la uniform initialization syntax (sintassi di inizializzazione uniforme), che uniforma la sintassi per l'inizializzazione di oggetti o variabili con quella per gli array e permette di evitare ogni ambiguità con la dichiarazione di funzioni. La riga problematica del primo esempio può essere riscritta come:
TimeKeeper time_keeper{Timer{}};
Note
modifica- ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §8.2 Ambiguity resolution [dcl.ambig.res]
- ^ Scott Meyers, Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library, Addison-Wesley, 2001, ISBN 0-201-74962-9.
Collegamenti esterni
modifica- The Most Vexing Parse by Danny Kalev
- Amazing feats of Clang Error Recovery LLVM Project Blog
- C++'s "most vexing parse" ars technica
- Uniform initialization syntax and semantics