2008-09-09 10 views
6

Ein Freund und ich diskutierten C++ - Vorlagen. Er fragte mich, was das tun sollte:C++ - Vorlage Mehrdeutigkeit

#include <iostream> 

template <bool> 
struct A { 
    A(bool) { std::cout << "bool\n"; } 
    A(void*) { std::cout << "void*\n"; } 
}; 

int main() { 
    A<true> *d = 0; 
    const int b = 2; 
    const int c = 1; 
    new A<b> (c) > (d); 
} 

Die letzte Zeile in Main hat zwei sinnvolle Pars. Ist 'b' das Template-Argument oder ist b > (c) das Template-Argument?

Obwohl es trivial ist, dies zu kompilieren und zu sehen, was wir bekommen, haben wir uns gefragt, was die Mehrdeutigkeit löst?

Antwort

5

AFAIK würde es als new A<b>(c) > d kompiliert werden. Dies ist der einzige vernünftige Weg, um es IMHO zu analysieren. Wenn der Parser unter normalen Umständen nicht davon ausgehen kann, dass ein> Template-Argument endet, würde dies viel mehr Zweideutigkeit ergeben. Wenn Sie es anders wollen, haben Sie Folgendes geschrieben:

new A<(b > c)>(d); 
0

Die Gierigkeit des Lexers ist wahrscheinlich der entscheidende Faktor in der Abwesenheit von Klammern, um es explizit zu machen. Ich vermute, dass der Lexer nicht gierig ist.

+0

Aber ist nicht der lexing ganz eindeutig:

Dies natürlich Korrekturen, den Ärger mit mit Leerzeichen in verschachtelten Spezialisierungen hinzufügen? –

+0

Es wird mehrdeutig, also ja. Gierig oder nicht gierig zu sein macht dich nicht zweideutig. Gierigkeit ist eigentlich eine Möglichkeit, Mehrdeutigkeit aufzulösen. In diesem Fall geraten Sie auch in die Interaktion zwischen dem Lexer und dem Parser, also ist es vielleicht der Parser, der nicht gierig ist. –

+0

Die Grammatik kann mehrdeutig sein, aber der Lexer ist absolut nicht. Der Abstand macht den Lexer vollständig gut definiert. –

3

Die C++ Standard, der von einem < gefolgt für eine Template-Namen, wenn definiert, die < ist immer der Anfang der Vorlage Argumentliste und die erste nicht -nested > wird als das Ende der Vorlagenargumentliste genommen.

Wenn das Ergebnis des Operators > das Template-Argument sein soll, müssen Sie den Ausdruck in Klammern einschließen. Sie benötigen keine Klammern, wenn das Argument Teil eines static_cast<> oder eines anderen Vorlagenausdrucks ist.

6

Wie von Leon & Lee angegeben, definiert 14,2/3 (C++ '03) explizit dieses Verhalten.

C++ '0x fügt dem Spaß mit einer ähnlichen Regel hinzu, die auf >> zutrifft. Das Grundkonzept ist, dass, wenn eine Vorlage-Argument-Liste ein nicht verschachteltes >> als zwei verschiedene >> Token behandelt werden Parsen und nicht der rechte Shift-Operator:

template <bool> 
struct A { 
    A(bool); 
    A(void*); 
}; 

template <typename T> 
class C 
{ 
public: 
    C (int); 
}; 

int main() { 
    A<true> *d = 0; 
    const int b = 2; 
    const int c = 1; 
    new C <A<b>> (c) > (d); // #1 
    new C <A<b> > (c) > (d); // #2 
} 

‚# 1‘ und ‚# 2‘ sind gleichwertig in den obigen.

C<A<false>> c; // Parse error in C++ '98, '03 due to "right shift operator" 
+0

Ich habe schon davon gehört. Einige Compiler haben die Pistole gesprungen, indem sie 'x >' sowieso erlauben. Aber ich frage mich nur: ist es tatsächlich in Bezug auf kontextsensitives Lexing definiert, oder durch eine Grammatikregel wie TemplateId '<' TemplateId '<' TemplateArgument '>>' an sich gegeben? – Stewart

+1

@ Stewart: Es ist das ehemalige (kontextsensitives Lexing). Zusammenfassend sagt 14.2/3, dass beim Parsen einer Template-Argument-Liste das erste nicht verschachtelte >> als zwei getrennte> Token behandelt wird. Sie können das eigentliche Papier für weitere Informationen lesen: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html –