Il most vexing parse è una forma di soluzione di ambiguità sintattica del C++, definita nella sezione 8.2 dello standardISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §8.2 Ambiguity resolution [dcl.ambig.res], 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).
Esempio con classi e oggetti Il 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 di classe , inizializzata con un'istanza anonima della classe ; la dichiarazione di una funzione con tipo di ritorno e un singolo parametro (senza nome) di tipo puntatore a funzione senza parametri e con tipo di ritorno .
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 è una funzione dichiarata alla riga precedente, per cui non è possibile chiamare su essa il metodo .
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 Un 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, 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
Lo 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
Collegamenti esterni The Most Vexing Parse by Danny Kalev Amazing feats of Clang Error Recovery LLVM Project Blog C++'s "most vexing parse" ars technica
Categoria:C++