expected primary-expression before ‘>’
-
Hi,
folgender Code:
struct FooCreator { template<typename T> static T Create(int i) { return T(i); } }; struct Foo { Foo(int i) { } }; template<typename FooT, typename FooCreatorT> struct Bar { Bar() { FooCreatorT::Create<FooT>(1); // error: expected primary-expression before ‘>’ token } }; int main() { Bar<Foo, FooCreator> bar; }
Gcc 4.5 meinte dazu
error: expected primary-expression before ‘>’ token
Frage: Warum? Was genau soll mir das sagen? Wie fixe ich das?
Vielen Dank.
-
Versuchs mal so:
template<typename FooT, typename FooCreatorT> struct Bar { Bar() { FooCreatorT::template Create<FooT>(1); // error: expected primary-expression before ‘>’ token } };
-
@Tachyon
Hast du da mal nen Link zu? Ich brauch das so selten, dass ich das immer wieder vergesse. Muss mir mal irgendwie ne Eselsbrücke dazu bauen.
-
DocShoe schrieb:
@Tachyon
Hast du da mal nen Link zu? Ich brauch das so selten, dass ich das immer wieder vergesse. Muss mir mal irgendwie ne Eselsbrücke dazu bauen.Ne, irgendwie nicht. Wenn man das eh selten braucht, kann man es auch nachgucken. Wenn man es oft braucht, dann weiß man das irgendwann.
-
Ist eigentlich eine relativ einfache Regel. Wenn du innerhalb eines Templates auf einen Typen/ein Template aus einem unbekannten Typen (hier FooCreatorT) zugreifen möchtest, musst du typename/template davor schreiben.
-
Versuchs wie der Compiler zu lesen:
FooCreatorT ist ein Templateargument, also eine Klasse, über die der Compiler an der Stelle NICHTS weiß. Garnichts. Wirklich.
<parse...>
FooCreatorT::Create
okay, was könnte das sein?
a) eine Membervariable oder Methode der Klasse
b) ein membertyp/typedef der Klasse
c) Anfang eines MembertemplatesAls Compiler gehen wir immer von a) aus, und dabei von einer Membervariablen, es sei denn wir stoßen auf Klammern, die andeuten, dass es sich um einen Funktionsaufruf handelt (oder um den Aufruf von op() eines Members) aber a) ist gesetzt.
<parse...>
FooCreatorT::Create<
Aha. Ein Vergleichsoperator.
<parse...>
FooCreatorT::Create<FooT
okay, jetzt wird vermutlich gleich ein Member der ebenso unbekannten Klasse FooT genannt...
<parse...>
FooCreatorT::Create<FooT>
WTF?
_______Okay, wie kriegen wir den Compiler/Parser dazu, nicht von Variante a) auszugehen?
b) mit vorangestelltem typename (typename FooCreatorT::Create)
c) mit eingefügtem template (FooCreatorT::template Create)Das ist schon alles.
-
Kann man sich auch aus der Arbeitsweise des Compilers klar machen, wenn man mal annimmt, dass dieser von oben nach unten vorgeht (was er auch so tut). Wenn er in Zeile 22 ist, dann weiß er bloß, dass FooCreatorT irgendein Typ ist. Und nun muss er entscheiden, ob das was der Programmierer da geschrieben hat Sinn macht. Und zwar eindeutig. Was wäre, wenn FooCreatorT::Create eine Membervariable wäre? Dann wäre das '<' als ein "kleiner"-Operator zu interpretieren. Oder wenn es ein typedef wäre? Dann könnte man die Konstruktion als falsch identifizieren und einen Fehler werfen, weil auf einen Typnamen kein < folgen kann. Oder wenn es, wie hier, ein Template ist? Dann wäre das '<' der öffnende Teil einer Templateklammer. Der richtige Kontext muss also vorher hergestellt werden.
Das Beispiel kommt etwas besser, wenn statt einem '<' ein '*' folgt, dann ist das Problem verständlicher:
http://pages.cs.wisc.edu/~driscoll/typename.html
-
Ohne Two-Phase-Lookup kann man das nicht erklären. Der Compiler versucht schon, einen Sinn in generischem Code zu erkennen, bevor er die Template-Argumente einsetzt. Dabei muss er bestimmte Annahmen machen, z.B. dass bestimmte von Template-Argumenten abhängige Bezeichner im Zweifelsfall kein Typ und auch kein Template sind. Das war nicht immer so in C++, deshalb fehlt dieses typename/template-Gedöns in altem Code oft.
-
typename FooCreatorT::Create
und
FooCreatorT::template Create
sind also äquivalent?
-
Nein, wieso? Ein Typ ist was anderes als ein Template.
-
Narf, jetzt hab´ ich´s auch kapiert
-
struct C { const static int i = 0; typedef int type; template <typename T> static void foo() {} template <typename T> void foo2() {} static void bar() {}; void bar2() {}; template <typename T> struct Inner { typedef double type; }; }; template <class Outer> struct D { const static int oi = Outer::i; //variante a) : i ist eine membervariable von a) void do_bar(Outer& o) { Outer::bar(); //variante a): bar() ist eine (statische) memberfunktion o.bar2(); //variante a): bar2() ist eine (nicht-statische) memberfunktion } typename Outer::type ot_var; //variante b): type ist ein member-typedef void do_foo(Outer& o) { Outer::template foo<long>(); //variante c): foo ist ein (statisches) memberfunktons-TEMPLATE o.template foo2<bool>(); //variante c): foo ist ein (nicht-statisches) memberfunktions-TEMPLATE } typename Outer::template Inner<bool>::type it_var; //variante c): Inner ist ein memberklassen-TEMPLATE //plus b): Inner<bool>::type ist ein typedef }; int main() { D<C> dc; dc.ot_var = 1; dc.it_var = 2; C c; dc.do_foo(c); dc.do_bar(c); }
-
Danke für die schnellen Antworten. Ich hatte schon alle Versionen von 'typename' durch und mittlerweile weisst mich der Compiler bei einem fehlenden 'typename' auch darauf hin ('typename needed here' oder sowas) - aber auf template bin ich nicht gekommen und da sagt der Compiler garnichts zu.