Programiersprache für Anfänger
-
DEvent schrieb:
Der Compiler muss erst bin ins Letzte uebersetzen um am Ende zu merken dass das Interface nicht stimmt oder ein template Typ A nicht mit template Typ B uebereinstimmt, obwohl es so sein muesste.
Hauptsache er merkt es. Soll man dir jetzt den Unterschied zwischen statischer und dynamischer Typprüfung erklären?
-
Bashars primäre Charaterzüge:
+besserwisserisch
+arrogant
+unlustig
-
Ein Template ist kein Type. Eine Instanzierung eines Template schon.
Mit diese Vorlage:
template <class T> T GetMax (T a, T b) { T result; result = (a>b)? a : b; return (result); }
wird durch diese Anwendung:
int main() { GetMax(5,1); }
diese zur Compilezeit erstellt:
int GetMax (int a, int b) { int result; result = (a>b)? a : b; return (result); }
Wo ist die Typenunsicherheit? O.o
-
DEvent schrieb:
Du hast mich nicht verstanden. Bei
template <class T> T GetMax (T a, T b) { T result; result = (a>b)? a : b; return (result); }
wird kein Typ geprueft. T kann alles sein, der Compiler prueft nur, ob das Interface stimmt. Das ist ja fuer mich der Widerspruch zu dem restlichen Konzept von C++. Templates fuehren ein Konzept von z.B. JavaScript in C++ hinein. IMHO ist das dann die Ursache der kryptischen Fehlermeldungen. Der Compiler muss erst bin ins Letzte uebersetzen um am Ende zu merken dass das Interface nicht stimmt oder ein template Typ A nicht mit template Typ B uebereinstimmt, obwohl es so sein muesste.
Entweder verstehe ich dein Posting nicht oder du hast Templates nicht verstanden.
Also, der Reihe nach: Template Typen gibts nicht. T ist als Beispiel ein int. somit ist a und b 100%ig vom gleichen Typ. Steht ja in der GetMax-Signatur!
Nun, was muss T erfüllen? T muß den Operator > implementiert haben. Was z.B. bei int der Fall ist. Kennt int Operator > nicht, wird dir das der Compiler melden.
Heutige Compiler (nein, nicht die von vor 10 Jahren), werden dir recht verständliche Meldungen ausspucken. Die Compiler wurden in dem Bereich weit verbessert.
Ich habe mal dein Beispiel absichtlich fehlerhaft benutzt (anstatt int, mit einer Dummy-Klasse Test die keinen passenden Operator hat). Die Fehlermeldung von MSVC 2005:
test.cpp(18) : error C2676: binary '>' : 'Test' does not define this operator or a conversion to a type acceptable to the predefined operator
test.cpp(25) : see reference to function template instantiation 'T GetMax<Test>(T,T)' being compiled
with
[
T=Test
]Yo, das sollte doch recht unkryptisch sein. Oder?
Für C++0x sind die sogenannten Concepts vorgesehen. D.h. der Template-Entwickler kann explizit die Bedingungen vorgeben. Er kann als Bedingung sagen, T muß Operator > implementiert haben. Wenn nicht, fängt der Compiler erst garnicht an das Template zu kompilieren, und spuckt den Fehler als Bedingung aus. Der User des Templates kann genau ablesen, was erwartet wurde. Ein User selbst braucht aber nicht wissen, wie man Concepts anwendet.
Also auch hier wird sich was in der Zukunft tun.
-
~john schrieb:
~fricky schrieb:
aber nur in seltenen ausnahmen. ein objekt, auf das es keinen verweis mehr gibt, oder objekte die sich gegenseitig referenzieren, aber auf keins davon mehr ein verweis im aktiven code besteht, schmeisst der GC irgendwann automatisch weg.
Du hast den wichtigen Punkt übersehen "auf das es keinen verweis mehr gibt". Wenn diese Bedingung erfüllt ist, wir mit RC das Objekt ebenfalls abgeräumt. Die Ausnahme sind zyklische Strukturen, dann und nur dann hat ein GC einen echten Vorteil. Aber der ursprüngliche Einwand war, daß man mit RC irgend wo noch eine Referenz auf das Objekt hätte, und so es nicht zerstört würde. Exakt bei so einem Fall bietet der GC keinen Vorteil, da das Objekt nämlich immer noch referenziert wird, und er es ebenfalls nicht zerstören kann!
Jo.
Dazu kommt dass GC einen grundsätzlichen und einen effektiven Nachteil hat.
Der grundsätzliche: keine deterministische Finalisierung (wurde sicher schon 100x erwähnt aber egal).
Der effektive (im Sinn von: man hat das Problem mit existierenden Implementierungen, wie z.B. .NET): der GC räumt auch gerne mal Objekte weg die eigentlich noch gebraucht werden, und finalisiert diese auch munter. z.B. Objekte "auf denen" gerade noch eine Member-Funktion ausgeführt wird, einfach deswegen weil diese Member-Funktion das Objekt danach nichtmehr "angreift". Was bei Objekten ohne Finalizer auch egal ist. IMO aber eine sehr dumme Sache wenn man einen Finalizer hat, der z.B. irgendeine unmanaged Resource freigibt, obwohl noch eine native Funktion damit arbeitet. Natürlich kann man dem GC das ausreden, nur kommt es schnell vor dass man irgendwo eine Stelle übersieht wo es nötig gewesen wäre. Und debuggen lassen sich solche Fehler nahezu garnicht.
IMO ein ganz krasser Nachteil von GC Systemen. Betrifft wie gesagt nicht GC grundsätzlich, sondern nur die Implementierungen die das so machen.
-
@HansiHinterseher:
DEvent schreibt immer gerne zum Thema C++, obwohl er C++ weder mag noch sonderlich gut versteht
-
hustbaer schrieb:
@HansiHinterseher:
DEvent schreibt immer gerne zum Thema C++, obwohl er C++ weder mag noch sonderlich gut versteht
So sind sie eben, unsere Linux Fanboys
-
HansiHinterseher schrieb:
Also, der Reihe nach: Template Typen gibts nicht. T ist als Beispiel ein int. somit ist a und b 100%ig vom gleichen Typ. Steht ja in der GetMax-Signatur!
Nun, was muss T erfüllen? T muß den Operator > implementiert haben. Was z.B. bei int der Fall ist. Kennt int Operator > nicht, wird dir das der Compiler melden.
Heutige Compiler (nein, nicht die von vor 10 Jahren), werden dir recht verständliche Meldungen ausspucken. Die Compiler wurden in dem Bereich weit verbessert.
Ich habe auch nichts anderes behauptet. Fuer T kann ich ein int, float oder eben ein String einsetzen. Der Compiler ueberprueft den Typen nicht, sondern nur das Interface. Wenn mein eingesetzter Type eben den operator< nicht implementiert, also das Interface nicht stimmt, dann gibt es ein Fehler. Das ist das gleiche Verhalten wie z.B. in JavaScript.
-
DEvent schrieb:
Das ist das gleiche Verhalten wie z.B. in JavaScript.
Der Unterschied zwischen Compilierzeit- und Laufzeitfehler ist dir bekannt?
-
DEvent schrieb:
Fuer T kann ich ein int, float oder eben ein String einsetzen....Das ist das gleiche Verhalten wie z.B. in JavaScript.
naja, in javascript würde ein stringvergleich wahrscheinlich gehen. dieses c++template würde aber bei
GetMax("hallo","doof")
pointer vergleichen, d.h. für char* braucht man 'ne spezialversion, die 'strcmp' oder sowas benutzt. das wäre eine kleine stolperfalle für anfänger.
-
~fricky schrieb:
naja, in javascript würde ein stringvergleich wahrscheinlich gehen. dieses c++template würde aber bei
GetMax("hallo","doof")
pointer vergleichen, d.h. für char* braucht man 'ne spezialversion, die 'strcmp' oder sowas benutzt. das wäre eine kleine stolperfalle für anfänger.
Wobei man normalerweise davon ausgeht, das std::max nicht mit bekannten Werten aufgerufen wird (Sonst könnte man das ergebnis auch direkt hinschreiben). Sprich, in der Regel sieht es eher so aus (ganz davon abgesehen das strcmp C und nicht C++ ist):
std::string a = "Hallo"; // Initialisierungen stellvertretend für Codeaufrufe std::string b = "Welt"; // zwischendrin std::string ergebnis = std::max(a, b);
Und das liefert das erwartete Erebnis.
-
asc schrieb:
Und das liefert das erwartete Erebnis.
ok, aber
const char *m = std::max("hello", "world")
geht trotzdem nicht, oder täusche ich mich? macht std::max dann auch 'nen pointervergleich?
-
DEvent schrieb:
Ich habe auch nichts anderes behauptet. Fuer T kann ich ein int, float oder eben ein String einsetzen. Der Compiler ueberprueft den Typen nicht, sondern nur das Interface. Wenn mein eingesetzter Type eben den operator< nicht implementiert, also das Interface nicht stimmt, dann gibt es ein Fehler. Das ist das gleiche Verhalten wie z.B. in JavaScript.
Sorry, kenne JavaScript leider nicht. Kann dazu also nichts sagen. Aber bei C++ ist es ein gutes und gewolltes Verhalten, das ich einen Compiler-Error erhalte. Also weiß ich jetzt auch nicht, was dich wirklich daran stört, wenn du sagst "wie in JavaScript".
-
~fricky schrieb:
asc schrieb:
Und das liefert das erwartete Erebnis.
ok, aber
const char *m = std::max("hello", "world")
geht trotzdem nicht, oder täusche ich mich? macht std::max dann auch 'nen pointervergleich?
Das wird so nicht funktionieren, da er ja kein const char* zurück liefert, sondern entsprechend dem impliziten Template-Parameter ein const char[6].
Hem...
-
HansiHinterseher schrieb:
~fricky schrieb:
asc schrieb:
Und das liefert das erwartete Erebnis.
ok, aber
const char *m = std::max("hello", "world")
geht trotzdem nicht, oder täusche ich mich? macht std::max dann auch 'nen pointervergleich?
Das wird so nicht funktionieren, da er ja kein const char* zurück liefert, sondern entsprechend dem impliziten Template-Parameter ein const char[6].
dann braucht's wohl doch eine selbstgestrickte variante, wie etwa das:
template <> const char* max(const char* a, const char* b) { return strcmp(a, b) > 0 ? a : b ; }
^^ausserdem noch in den std-namespace verfrachten.
-
HansiHinterseher schrieb:
~fricky schrieb:
asc schrieb:
Und das liefert das erwartete Erebnis.
ok, aber
const char *m = std::max("hello", "world")
geht trotzdem nicht, oder täusche ich mich? macht std::max dann auch 'nen pointervergleich?
Das wird so nicht funktionieren, da er ja kein const char* zurück liefert, sondern entsprechend dem impliziten Template-Parameter ein const char[6].
Hem...Klar geht das:
const char* i = std::max("aaa", "aaa");
Der Compiler liefert ja auch den Typen, wenn ich fuer i den falschen Typen einsetze:
test2.cpp:42: error: invalid conversion from 'const char*' to 'int'
Allerdings bezweilfe ich stark, dass der Code das macht, was der Programmierer damit gemeint hat.
-
DEvent schrieb:
Klar geht das:
const char* i = std::max("aaa", "aaa");
Der Compiler liefert ja auch den Typen, wenn ich fuer i den falschen Typen einsetze...
wie jetzt? der eine sagt es geht nicht, der andere sagt, es geht. also was nun?
-
Ob es geht hängt wohl vom Compiler ab. Mit MSVC 2005 geht es z.B. nicht.
-
hustbaer schrieb:
Ob es geht hängt wohl vom Compiler ab. Mit MSVC 2005 geht es z.B. nicht.
ist das nicht eher eine frage der stl-implementation? wie auch immer, c++ ist für anfänger absolut nicht geeignet. ich hoffe, darüber sind wir uns jetzt einig. ihr seht ja selbst, welch einfache dinge (im vergleich zu anderen sprachen/systemen) in c++ viele, viele fragen aufwerfen.
-
ist das nicht eher eine frage der stl-implementation?
Hm. Pfuh. Ja, das zumindest auch. Obwohl ...
Beispiel (mit MSVC 2005):
#include <iostream> // geht mit array-typen template <class T> T const& min1(T const& a, T const& b) { std::cout << "T = " << typeid(T).name() << std::endl; if (a < b) // beim vergleich zerfällt "char const (&)[2]" anscheinend zu "char const*", return a; // es werden also zeiger verglichen, nicht der array-inhalt else return b; } // geht NICHT mit array-typen template <class T> T const& min2(T const& a, T const& b) { std::cout << "T = " << typeid(T).name() << std::endl; return a < b ? a : b; // MSVC 2005 ermittelt hier als typ des ausdrucks // "bool ? char const (&)[2] : char const (&)[2]" anscheinend // einfach "char const*" - warum auch immer } // geht mit array-typen, obwohl's IMO eigentlich äquivalent zu min2 sein sollte template <class T> T const& min3(T const& a, T const& b) { std::cout << "T = " << typeid(T).name() << std::endl; return a < b ? static_cast<T const&>(a) : static_cast<T const&>(b); } template <class T> void show_type(T) { std::cout << "T = " << typeid(T).name() << std::endl; } template <class T> void show_type_cref(T const&) { std::cout << "T = " << typeid(T).name() << std::endl; } int main() { show_type("foo"); char* s = "foo"; // geht (korrekter-, wenn auch unsinnigerweise)! man beachte aber dass show_type("foo") "char const *" als Typ ausgibt! show_type_cref("foo"); typedef char const (&T)[2]; T x = min1("a", "b"); std::cout << x << std::endl; T y = min1("d", "c"); std::cout << y << std::endl; // T z = min2(a, b); // geht nicht T z = min3("e", "f"); // geht wieder?!? return 0; }
Output:
T = char const * T = char const [4] T = char const [2] b T = char const [2] c T = char const [2]
Bei einigen Dingen bin ich mir hier nicht ganz sicher...
- Welcher Typ sollte bei show_type("foo") rauskommen?
- Welcher Typ sollte bei show_type_cref("foo") rauskommen?
- Sollte ein "const-ref-auf-array" Typ bei "a < b ? a : b" wirklich zu "T const*" zerfallen??? Und darf es einen Unterschied machen ob man explizit nochmal nen Cast schreibt (min2 vs. min3)? IMO müsste sich zumindest min2 gleich wie min3 verhalten...
- Muss std::min die Parameter (und den Returnwert) per const-ref nehmen, oder wäre ein einfaches "template <class T> T min(T, T)" auch OK?
Und nochmal zu 1: IMO dürfte der Typ auf jeden Fall nicht const sein, da ein String-Literal laut Standard nicht const ist. Es ist zwar "gut" dass wenigstens hier das const "magischerweise" dazukommt, da die Regel "String Literale sind nicht const" sowieso Blödsinn ^4 ist, aber komisch ist es schon irgendwie.
Alles sehr seltsam...
wie auch immer, c++ ist für anfänger absolut nicht geeignet
ACK. Wobei hinzuzufügen ist dass Anfäger in jeder Sprache kaum ein 100% korrektes Programm hinbekommen werden, welches auch alle Spezial- und Fehlerfälle korrekt behandelt