Most vexing parse

risoluzione di un tipo di ambiguità sintattica nel C++

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 modifica

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

  1. la definizione di una variabile time_keeper di classe TimeKeeper, inizializzata con un'istanza anonima della classe Timer;
  2. la dichiarazione di una funzione time_keeper con tipo di ritorno TimeKeeper e un singolo parametro (senza nome) di tipo puntatore a funzione senza parametri e con tipo di ritorno Timer.

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 modifica

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, 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 modifica

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 modifica

Collegamenti esterni modifica

  Portale Informatica: accedi alle voci di Wikipedia che trattano di informatica