Che cosa è esattamente “rotto” con l’istanziazione di template a due fasi di Microsoft Visual C ++?

Leggendo domande, commenti e risposte su SO, sento sempre che MSVC non implementa correttamente la ricerca / creazione di modelli in due fasi.

Da quanto ho capito finora, MSVC ++ esegue solo un controllo di syntax di base sulle classi e le funzioni dei modelli e non controlla che i nomi utilizzati nel modello siano stati dichiarati almeno o qualcosa del genere.

È corretto? Cosa mi manca?

Mi limiterò a copiare un esempio dal mio “taccuino”

int foo(void*); template struct S { S() { int i = foo(0); } // A standard-compliant compiler is supposed to // resolve the 'foo(0)' call here (ie early) and // bind it to 'foo(void*)' }; void foo(int); int main() { S s; // VS2005 will resolve the 'foo(0)' call here (ie // late, during instantiation of 'S::S()') and // bind it to 'foo(int)', reporting an error in the // initialization of 'i' } 

Il codice sopra dovrebbe compilare in un compilatore C ++ standard. Tuttavia, MSVC (2005 e 2010 Express) segnalerà un errore a causa dell’implementazione errata della ricerca a due fasi.


E se guardi più da vicino, il problema è in realtà a due livelli. In superficie, è ovvio che il compilatore di Microsoft non riesce a eseguire una ricerca (prima fase) per un’espressione non dipendente foo(0) . Ma quello che fa dopo non si comporta veramente come una corretta implementazione della seconda fase di ricerca.

La specifica del linguaggio afferma chiaramente che durante la seconda fase di ricerca solo gli spazi dei nomi nominati da ADL vengono estesi con ulteriori dichiarazioni accumulate tra il punto di definizione e il punto di istanziazione. Nel frattempo, la ricerca non ADL (ovvero la ricerca di nomi non qualificati ordinari) non viene estesa dalla seconda fase, ma vede ancora quelle e solo quelle dichiarazioni che erano visibili nella prima fase.

Ciò significa che nell’esempio sopra il compilatore non dovrebbe vedere anche void foo(int) nella seconda fase. In altre parole, il comportamento di MSVC non può essere descritto da un semplice “MSVC rimanda tutte le ricerche alla seconda fase”. Ciò che MSVC implementa non è una corretta implementazione della seconda fase.

Per illustrare meglio il problema, prendere in considerazione il seguente esempio

 namespace N { struct S {}; } void bar(void *) {} template  void foo(T *t) { bar(t); } void bar(N::S *s) {} int main() { N::S s; foo(&s); } 

Si noti che anche se la chiamata a bar(t) all’interno della definizione del modello è un’espressione dipendente risolta nella seconda fase di ricerca, dovrebbe comunque risolversi nella void bar(void *) . In questo caso ADL non aiuta il compilatore a trovare la void bar(N::S *s) , mentre la normale ricerca non qualificata non dovrebbe essere “estesa” dalla seconda fase e quindi non dovrebbe vedere la void bar(N::S *s) entrambi.

Tuttavia, il compilatore di Microsoft risolve la chiamata alla void bar(N::S *s) . Questo non è corretto

Il problema è ancora presente nella sua gloria originale in VS2015.

Il progetto Clang ha una buona conoscenza della ricerca in due fasi e quali sono le varie differenze di implementazione: http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html

Versione breve: ricerca in due fasi è il nome del comportamento definito dallo standard C ++ per la ricerca del nome all’interno del codice del modello. Fondamentalmente, alcuni nomi sono definiti come dipendenti (le regole per cui sono un po ‘confusi), questi nomi devono essere cercati durante l’istanziazione del modello, e i nomi indipendenti devono essere cercati durante l’analisi del modello. Questo è sia difficile da implementare (apparentemente), sia da confondere per gli sviluppatori, quindi i compilatori tendono a non implementarlo nello standard. Per rispondere alla tua domanda, sembra che Visual C ++ ritardi tutte le ricerche, ma cerca sia il contesto del modello che il contesto di istanziazione, quindi accetta un sacco di codice che lo standard dice che non dovrebbe. Non sono sicuro che non accetti il ​​codice, o, peggio, lo interpreterà diversamente, ma sembra ansible.

Storicamente gcc non ha implementato correttamente la ricerca del nome in due fasi. È apparentemente molto difficile da raggiungere, o almeno non c’era molto incentivo …

  • gcc 4.7 afferma di implementarlo correttamente , finalmente
  • CLang mira a implementarlo, escludendo bug, è fatto su ToT e entrerà in 3.0

Non so perché i writer VC ++ non abbiano mai scelto di implementarlo correttamente, l’implementazione di un comportamento simile su CLang (per la microsoft compabitility) suggerisce che potrebbe esserci qualche miglioramento nelle prestazioni per ritardare l’istanziazione dei template alla fine dell’unità di traduzione (che non significa implementare la ricerca in modo errato, ma renderlo ancora più difficile). Inoltre, data l’apparente difficoltà di una corretta implementazione, potrebbe essere stata più semplice (e meno costosa).

Vorrei notare che VC ++ è il primo, e soprattutto, un prodotto commerciale. È guidato dalla necessità di soddisfare i propri clienti.

risposta breve

Disabilitare le estensioni della lingua con / Za

risposta più lunga

Stavo indagando su questo problema ultimamente ed è stato stupito che in VS 2013 l’esempio seguente di standard [temp.dep] p3 produca risultati errati:

 typedef double A; template class B { public: typedef int A; }; template struct X : B { public: A a; }; int main() { X x; std::cout < < "type of a: " << typeid(xa).name() << std::endl; } 

stamperà:

 type of a: int 

mentre dovrebbe essere stampato double . La soluzione per rendere conforms allo standard VS è disabilitare le estensioni della lingua (opzione / Za), ora il tipo di xa si risolverà a raddoppiare, anche gli altri casi di usare nomi dipendenti dalle classi di base saranno conformi allo standard. Non sono sicuro che ciò consenta la ricerca a due fasi.

Ora che MSVC ha implementato la maggior parte della ricerca dei nomi in due fasi, spero che questo post del blog risponda completamente a questa domanda: la ricerca dei nomi in due fasi arriva su MSVC (VC ++ blog)