Template-Methode mit Default-Parameter
-
Hmm. Mir scheint als hätte ich den Fehler gefunden, den VC da macht.
Die Funktionen selbst sind denke ich korrekt implementiert, aber der Aufruf der Funktionen scheint nicht so ganz zu klappen.foo.bar(2); 00EA10AB push 0 % zweiter parameter: ok 00EA10AD push 2 % erster parameter: ok 00EA10AF lea ecx,[foo] % this: ok 00EA10B2 call ForceBug::bar<int> (0EA1046h) foo.bar(2.0); 00EA10B7 push 0 % (1) 00EA10B9 sub esp,8 00EA10BC fld qword ptr [__real@4000000000000000 (0EA6830h)] % parameter 1 00EA10C2 fstp qword ptr [esp] % parameter 1 speichern 00EA10C5 lea ecx,[foo] % this 00EA10C8 call ForceBug::bar<double> (0EA103Ch)Da sieht man, dass bar(2) korrekt aufgerufen wird. Jedoch bin ich bei (1) stutzig geworden. Warum wird da lediglich ein Wort gepusht und nicht 2, wie es bei einem double ja sein müsste?
Also habe ich das ganze mal umgedreht:
foo.bar(2.0); 00EE10AB sub esp,8 00EE10AE fldz % schon eher wenn man floats ausnullen will 00EE10B0 fstp qword ptr [esp] 00EE10B3 sub esp,8 00EE10B6 fld qword ptr [__real@4000000000000000 (0EE6830h)] 00EE10BC fstp qword ptr [esp] 00EE10BF lea ecx,[foo] 00EE10C2 call ForceBug::bar<double> (0EE103Ch) foo.bar(2); 00EE10C7 sub esp,8 00EE10CA fldz % (2) 00EE10CC fstp qword ptr [esp] 00EE10CF push 2 % erster parameter 00EE10D1 lea ecx,[foo] 00EE10D4 call ForceBug::bar<int> (0EE1046h)Bei (2) dachte ich dann nur WTF? - Warum wird da eine floating Point Operation gemacht, wenn doch nur int's im Spiel sind?
Da scheint irgendwie Code vorschnell erzeugt zu werden.. Ok, also mal schauen was passiert, wenn wir die bösen templates weglassen. :
(ich habe das template mit identischen Funktionen mit expliziten Parametern ausgetauscht)foo.bar(2.0); 00C6168B sub esp,8 00C6168E fldz 00C61690 fstp qword ptr [esp] 00C61693 sub esp,8 00C61696 fld qword ptr [__real@4000000000000000 (0C66830h)] 00C6169C fstp qword ptr [esp] 00C6169F lea ecx,[foo] 00C616A2 call ForceBug::bar (0C61050h) foo.bar(2); 00C616A7 push 0 % kein floating Zeugs mehr. 00C616A9 push 2 00C616AB lea ecx,[foo] 00C616AE call ForceBug::bar (0C6104Bh)ahh.. schon eher..
und andersrum:
foo.bar(2); 008A168B push 0 008A168D push 2 008A168F lea ecx,[foo] 008A1692 call ForceBug::bar (8A104Bh) foo.bar(2.0); 008A1697 sub esp,8 008A169A fldz % jetzt passt auch das hier 008A169C fstp qword ptr [esp] 008A169F sub esp,8 008A16A2 fld qword ptr [__real@4000000000000000 (8A6830h)] 008A16A8 fstp qword ptr [esp] 008A16AB lea ecx,[foo] 008A16AE call ForceBug::bar (8A1050h)Jetzt stimmts auch in der Richtung.
Fazit: Selbst VC++ 2010 hat gewisse Probleme mit templates..

@SeppJ:
Danke, aber ich hab den Fehler, den VC da macht bereits.
Hat jemand eine Ahnung, ob der Bug bereits gemeldet wurde? Respektive wo es da eine gute DB dazu gibt? Ich werde das bei Gelegenheit mal noch in meinem Blog posten. Wird Zeit, dass da wiedermal was passiert.

//EDIT:
Habe anscheinend den korrekten Anlaufpunkt gefunden und der Fehler scheint noch nicht bekannt zu sein:
https://connect.microsoft.com/VisualStudio/Feedback
-
scary...
@drakon: Hast du den Fehler reported?
-
-
hustbaer schrieb:
scary...
@drakon: Hast du den Fehler reported?
Nein. Bin gester nicht dazu gekommen. Wollte eigentlich einen exakten Beschreib (auch für meinen Blog) von dem Fehler machen.
Ich habe mich gestern dort extra noch angemeldet.

@djohn:
Da ich ja bereits den Fehler analysiert habe wäre es vielleicht gut das auch noch zu melden. Dann können die sich ein wenig Arbeit sparen.. ^^
Ich werde mal in einem Kommentar den Fehlerbeschreib ergänzen.
-
Habe gerade noch ein paar Sachen ausprobiert. Mit freien template Funktionen funktioniert es und auch wenn man die Definition inline macht (was denke ich der Hauptgrund ist, dass den Fehler noch niemand bemerkt hat).
-
Der Bug tritt mit freien Funktionen genau so auf:
template<typename NumericType> void foobar(NumericType value, NumericType step = NumericType()); template<typename NumericType> void foobar(NumericType value, NumericType step) { NumericType dummy = step; std::cout << value << ", " << step << std::endl; } int main() { foobar(2); foobar(3.14); return 0; } // output: // 2, 0 // 4.27698e+086, 1.16762e-307Scheint aber wirklich davon abhängig zu sein dass Deklaration und Definition getrennt sind. Und ich kann's auch nicht mit etwas anderem als
doublereproduzieren. Und das auch nur, wenn vorher oder nachher ein Aufruf mit etwas anderem alsdoubleerfolgt.Auch nicht uninteressant folgendes:
template<typename NumericType> void foobar(NumericType value, NumericType step = 2); // <- hier direkt 2 statt NumericType() (0 oder andere Integer gehen genau so) template<typename NumericType> void foobar(NumericType value, NumericType step) { NumericType dummy = step; std::cout << value << ", " << step << std::endl; } int main() { foobar(2); foobar(3.14); return 0; } // output: // 2, 2 // 4.27698e+086, 3.75217e-308Immer noch (fast) der selbe falsche Output.
Wenn man als Default-Wert allerdings ein
doubleLiteral hinschreibt...template<typename NumericType> void foobar(NumericType value, NumericType step = 2.71828); // <- jetzt mit double literal hier template<typename NumericType> void foobar(NumericType value, NumericType step) { NumericType dummy = step; std::cout << value << ", " << step << std::endl; } int main() { foobar(2); foobar(3.14); return 0; } // output: // 2, 1949120276 // 3.14, 2.71828Auch falsch, aber ganz anders falsch

-
Der 64 Bit Compiler scheint übrigens genau so betroffen zu sein.
-
hustbaer schrieb:
Der Bug tritt mit freien Funktionen genau so auf:
Ah. Ich habe das nur mit direkt definierten freien Funktionen probiert.
Der 64 Bit Compiler scheint übrigens genau so betroffen zu sein
f.bar(2); 000000013F39165B xorpd xmm2,xmm2 000000013F39165F mov edx,2Uh, oh. Ja.. sieht so aus.. ^^
Aber es gibt keinen Laufzeitfehler bei mir.Scheint wirklich hauptsächlich an der Trennung zu liegen. Zum Glück implementieren die meisten templates direkt.

-
Tjaaah... die meisten.
In einem grösseren Projekt das ich gerade entwickle (mit VC 2005) hab' ich *einige* Templates mit getrennter Deklaration und Definition. Zwecks übersichtlichkeit der Header Files (Definition steht in eigenen .inl Files).
Und auch mit Default-Parametern und die Variablen sind auch oft vom Typdouble
EDIT:
Wobei... dort werden eigentlich nur doubles verwendet, und nie was anderes. Sollte dann OK sein. Hab auch noch keinen derartigen Fehler beobachtet. Doof ist es trotzdem. /EDIT
-
Beim Versuch ein Minimalbeispiel zu erstellen, konnte ich den Fehler bei freien Funktionen auch nicht reproduzieren. Hatte da wahrscheinlich wie drakon noch eine der anderen Bedingungen wegreduziert. Bei Deiner zweiten Variante (Default-Wert ist ein int) reicht es aus, nur eine Template-Instanz mit double zu erzeugen um zumindest bei VS2005 den Fehler zu provozieren. Die zusätzliche Bedingung, dass man mindestens 2 Template-Instanzen mit unterschiedlichen Typen für den Fehler braucht, fällt dann weg.
Ich hatte auch nur wegen der Übersichtlichkeit Deklaration und Definition getrennt, kein großes Problem, dass jetzt zu ändern. Aber wenn so ein Fehler erstmal auftritt, ist man natürlich unsicher, unter welchen Bedingungnen noch dieses undefinierte Verhalten auftreten kann.
Deshalb, nochmal Danke an alle die sich an der Diskussion beteiligt haben, ich denke, wir haben jetzt ganz gut eingegrenzt, wann der Fehler auftritt.djohn
-
Noch was lustiges...
Folgendes compiliert mit VS 2010:#include <iostream> int g_i = 42; template<typename T> void baz(T a1, T* a2 = &g_i); // <- default argument should only work for T = int template<typename T> void baz(T a1, T* a2) { std::cout << a1 << ", " << *a2 << std::endl; } class c {}; std::ostream& operator << (std::ostream& os, c const&) { return os; } int main() { baz(2); baz(3.14); // compiles OK, but should not baz("test"); // compiles OK, but should not baz(c()); // compiles OK, but should not return 0; }Beim Ausführen kommt natürlich nur Mist raus...
