2012-05-20 7 views
18

Ich habe festgestellt, dass beim Zugriff auf ein Nichtvorlagenattribut (v.foo) aus einer Variablen eines Vorlagentyps (T& v) C++ dazu verleitet werden kann zu denken, dass es eine Membervorlage ist ist eine gleichnamige Template-Funktion (template class <T> void foo()). Wie kann dies anhand der C++ - Spezifikation erklärt werden? Betrachten Sie dieses einfache Programm:C++ verwirrender Attributname für Elementvorlage

#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; 
} 

Wir haben eine Template-Funktion foo_negative, die ein Objekt eines beliebigen Typs nimmt und bestimmt, ob seine foo Attribut negativ ist. Die main Funktion instanziiert foo_negative mit [T = X]. Dieses Programm kompiliert und läuft ohne Ausgabe. Jetzt

, fügen Sie diese Funktion an die Spitze des Programms:

template <class T> 
void foo() 
{ 
} 

es mit G ++ 4.6.3 Ergebnisse in dieser Compiler-Fehler Kompilieren:

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 

(Wo Linie 13 ist return v.foo < 0 und Zeile 25 ist assert(!foo_negative(x)).)

Clang erzeugt ähnliche Fehler.

Wat? Wie konnte das Hinzufügen einer nicht verwandten Funktion, die nie aufgerufen wird, einen Syntaxfehler in ein gültiges Programm einführen? Beim Parsen foo_negative kennt der Compiler den Typ v nicht. Entscheidend ist, ob v.foo eine Membervorlage oder ein reguläres Member ist. Offensichtlich muss es sich bei der Parsing-Zeit entscheiden (bevor die Vorlage instanziiert wird), ob sie als Elementvorlage oder als normales Element behandelt werden soll.

Wenn er denkt, v.foo Mitglied Vorlage ist, dann wird < 0 als vorbei 0 als Template-Argument gesehen, und es gibt eine fehlende >, daher der Syntaxfehler. Wenn dann foo_negative mit [T = X] instanziiert wird, gibt es einen weiteren Fehler, da X::foo keine Member-Vorlage ist.

Aber warum denkt es v.foo ist ein Mitglied Vorlage? Diese Ambiguität ist genau das, was das Schlüsselwort template ist: Wenn ich v.template foo schreiben würde, dann würde ich C++ explizit auffordern, eine Mitgliedervorlage zu erwarten, aber ich habe das Schlüsselwort template nicht verwendet! Ich habe mich nicht auf eine Mitgliedervorlage bezogen, daher sollte angenommen werden, dass es sich um ein reguläres Mitglied handelt. Die Tatsache, dass es eine Funktion mit dem gleichen Namen wie das Mitglied gibt, sollte keine Wirkung haben. Warum geht das? Es kann kein Fehler im Compiler sein, weil GCC und Clang konsistent sind.

+0

Das ist seltsam. Es kompiliert auf gcc-4.3.4: http://ideone.com/FP3SO –

+0

Nein, Sie haben vergessen, die 'Vorlage void foo()' Erklärung hinzuzufügen: http://ideone.com/FxEBm Es kompiliert nicht . – mgiuca

+4

vielleicht ist es wirklich ein Fehler sowohl in gcc und clang: [Deklaration] (http://ideone.com/zofCF). Ich änderte 'v.foo < 0' to '0 > v.foo' und es kompiliert fein. Vielleicht denken beide Compiler, dass "<0" ein Vorlagenargument ist, wie Sie in der Frage erwähnt haben. – user2k5

Antwort

2

In Clang war dies PR11856, die vor ~ 2,5 Monaten behoben wurde. Clang trunk und Clang 3.1 melden keine Fehler mit this code. Der Clang Fehler enthält ein explanation, warum dieser Code abgelehnt wurde, und warum der Code korrekt ist, hier wiedergegeben (leicht Ihren Fall zu adressieren gezwickt):

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

Danke. Ich habe gerade den [entsprechenden Fehlerbericht in GCC] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200) gefunden, der 2003 gemeldet wurde, aber noch nicht behoben. – mgiuca

4

Dies ist ein Fehler.

Ihr Code läuft auf MSVC (2011) einwandfrei. Ich denke, der Parser des Compilers übersetzte die '<' als Start-Token für eine Vorlage-Anweisung. Aber warum Clang und GCC haben diesen Fehler gleichzeitig?

Bitte melden Sie den Fehler here und here.

Vielleicht ist dies auch interessant: Another bug in g++/Clang? [C++ Templates are fun]

+0

Sind Sie sicher, dass es ein Fehler in GCC/Clang und nicht in MSVC ist? Wenn Sie zeigen möchten, dass es sich um einen Fehler handelt, müssen Sie ihn aus der Spezifikation und nicht aus einer anderen Implementierung heraus zeigen. Vor allem ein Microsoft - sie haben eine Geschichte, Dinge zu erlauben, die streng genommen nicht sollten. Ich habe die Spezifikation überflogen, aber es ist ziemlich schwer, einen Sinn zu ergeben (ich lese später mehr) - ich wette, dass es wahrscheinlicher ist, dass es in der Spezifikation (möglicherweise §14.2.3) eine winkelige Klausel gibt, die G ++ und Clang folgt dem Buchstaben und MSVC ignoriert, aber es könnte das Gegenteil sein. – mgiuca

+0

Und ich werde definitiv einen Fehler einreichen, wenn es gezeigt werden kann, dass sie die Spezifikation verletzen. Es ist nur, dass in meiner Erfahrung, wenn Sie denken, dass es einen Fehler im Compiler ist, ist es in der Regel entweder a) Ihren Code, oder b) Sie lesen die Spezifikation nicht genau genug. – mgiuca

+0

vielleicht ... aber die C++ Spezifikation ist riesig und es ist sehr kompliziert. Ich weiß, dass MSVC nicht der beste Compiler ist, der die C++ - Spezifikation erfüllt, aber ich denke, das Beste ist jetzt, den Bug zu melden ... vielleicht ist es oder es ist kein Bug ... – pearcoding

6

Es ist wie ein Compiler Fehler aussieht.

Der Standard sagt:

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.

und in 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.

Der Standard scheint nicht zu zeigen, dass die Namenssuche jemals eine Nicht-Mitglied Funktionsvorlage finden Hier. In jedem Fall sollte die Bedeutung von < zur Definitionszeit der Vorlage festgelegt werden, nicht zur Instanziierungszeit (es ist dann zu spät).

Verwandte Themen