2012-05-20 9 views
18

Ho scoperto che quando si accede a un attributo non modello (v.foo) da una variabile di un tipo di modello (T& v), C++ può essere indotto a pensare che si tratti di un modello membro è una funzione modello con lo stesso nome (template class <T> void foo()). Come può essere spiegato dalle specifiche C++? Considerate questo semplice programma:Nome attributo confuso C++ per il modello membro

#include <cassert> 

/** Determine whether the 'foo' attribute of an object is negative. */ 
template <class T> 
bool foo_negative(T& v) 
{ 
    return v.foo < 0; 
} 

struct X 
{ 
    int foo; 
}; 

int main() 
{ 
    X x; 
    x.foo = 5; 
    assert(!foo_negative(x)); 
    return 0; 
} 

Abbiamo una funzione template foo_negative che prende un oggetto di qualsiasi tipo e determina se l'attributo foo è negativo. La funzione main crea un'istanza di foo_negative con [T = X]. Questo programma si compila e funziona senza output.

Ora, aggiungere questa funzione per la parte superiore del programma:

template <class T> 
void foo() 
{ 
} 

compilazione con G ++ 4.6.3 risultati in questo errore di compilazione:

funcs.cpp: In function ‘bool foo_negative(T&)’: 
funcs.cpp:13:14: error: parse error in template argument list 
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’: 
funcs.cpp:25:5: instantiated from here 
funcs.cpp:13:14: error: ‘foo’ is not a member template function 

(dove la linea 13 è return v.foo < 0 e La riga 25 è assert(!foo_negative(x)).)

Clang produce errori simili.

Wat? In che modo l'aggiunta di una funzione non correlata mai chiamata riesce a introdurre un errore di sintassi in un programma valido? Durante l'analisi di foo_negative, il compilatore non conosce il tipo di v e, in modo cruciale, non sa se v.foo è un modello membro o un membro normale. Apparentemente, deve decidere al momento dell'analisi (prima che il modello venga istanziato) se trattarlo come un modello membro o un membro normale.

Se pensa v.foo è un membro template, quindi < 0 è visto come passare 0 come argomento di modello, e c'è manca un >, quindi l'errore di sintassi. Quindi, quando foo_negative viene istanziato con [T = X], c'è un altro errore perché X::foo non è un modello membro.

Ma perché pensa che lo v.foo sia un modello membro? Questa ambiguità è esattamente ciò che la parola chiave template è per: se avessi scritto v.template foo, avrei detto esplicitamente a C++ di aspettarmi un modello membro, ma non ho usato la parola chiave template! Non ho fatto riferimento a un modello membro, quindi dovrebbe presumere che sia un membro normale. Il fatto che sia presente una funzione con lo stesso nome del membro non dovrebbe avere alcun effetto. Perché lo fa? Non può essere un bug nel compilatore perché GCC e clang sono coerenti.

+0

Che strano. Compilare su gcc-4.3.4: http://ideone.com/FP3SO –

+0

No, hai dimenticato di aggiungere la 'modello void foo()' dichiarazione: http://ideone.com/FxEBm Non si compila . – mgiuca

+4

forse è davvero un bug sia in gcc che in clang: [dichiarazione] (http://ideone.com/zofCF). Ho cambiato 'v.foo < 0' to '0 > v.foo' e compila bene. Forse entrambi i compilatori pensano che "<0" sia argomento di template, come hai detto nella domanda. – user2k5

risposta

2

In Clang, questo era PR11856, che è stato risolto ~ 2,5 mesi fa. Clang trunk e Clang 3.1 non riportano alcun errore con this code. Il bug Clang include una explanation del motivo per cui era stato rifiutato questo codice, e perché il codice è corretto, qui riprodotta (leggermente modificato per affrontare il vostro caso):

The paragraph that matters here is [basic.lookup.classref]p1:

"In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a < , the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template."

Since v is dependent, presumably the identifier is not found so we consider what happens if we look in the context of the entire postfix-expression. Since we find a function template, we should not conclude that we have the start of a template-id.

+0

Grazie. Ho appena trovato il [rapporto bug corrispondente in GCC] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200) che è stato segnalato nel 2003, non ancora risolto. – mgiuca

4

Questo è un bug.

Il tuo codice funziona correttamente su MSVC (2011). Penso che il parser del compilatore abbia tradotto lo '<' come un token di partenza per un'istruzione template. Ma perché Clang e GCC hanno questo bug nello stesso tempo?

Si prega di segnalare l'errore here e here.

Forse questo è anche interessante: Another bug in g++/Clang? [C++ Templates are fun]

+0

Sei sicuro che sia un bug in GCC/Clang e non in MSVC? Se vuoi dimostrare che si tratta di un bug, devi mostrarlo dalla specifica, non da un'altra implementazione. Soprattutto uno di Microsoft: hanno una storia che consente di compilare cose che non dovrebbero assolutamente. Ho sfogliato le specifiche ma è piuttosto difficile dare un senso a (ne leggerò altre più avanti) - Scommetto che è più probabile che ci sia qualche clausola distorta nelle specifiche (forse §14.2.3) che G ++ e Clang sta seguendo la lettera e MSVC sta ignorando, ma potrebbe essere il contrario. – mgiuca

+0

E scriverò sicuramente un bug se si può dimostrare che violano le specifiche. È solo che, secondo la mia esperienza, se pensi che ci sia un bug nel compilatore, di solito è a) il tuo codice, oppure b) non stai leggendo le specifiche abbastanza da vicino. – mgiuca

+0

forse ... ma la specifica C++ è enorme ed è molto complicata. So che MSVC non è il miglior compilatore conforme alle specifiche C++, ma penso che la cosa migliore da fare ora sia riportare il bug ... forse lo è o non è un bug ... – pearcoding

6

Sembra proprio un bug del compilatore.

Lo standard dice:

After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator.

e 3.4.5/1

In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.

Lo standard non sembra indicare che la ricerca del nome può mai trovare un modello di funzione non membro Qui. In ogni caso, il significato di < deve essere deciso al momento della definizione del modello, non al tempo di istanziazione (è troppo tardi allora).

Problemi correlati