C++11 (aka C++0x) - Approval of Final Committee Draft
-
Gut finde ich, dass export und Exception specifications rausgeflogen sind.
Aber Concepts hätte ich schon gern gehabt. Oder zu mindest irgendetwas was den typename Foo::template Bar<typename Blub::Bla>-Quark leichter zu lesen macht. Ganz böse wird's, wenn man dann noch Typs einstreut welche keine Templateparameter sind.
-
Ben04 schrieb:
Gut finde ich, dass export und Exception specifications rausgeflogen sind.
Aber Concepts hätte ich schon gern gehabt. Oder zu mindest irgendetwas was den typename Foo::template Bar<typename Blub::Bla>-Quark leichter zu lesen macht. Ganz böse wird's, wenn man dann noch Typs einstreut welche keine Templateparameter sind.
export
ist weg? Exception specs sind auch weg? Yeehaa
-
evilissimo schrieb:
krümelkacker schrieb:
Anscheinend hat man sich auch dazu entschieden, mit den Begriffen lvalue/rvalue "aufzuräumen". Zumindest unterscheidet man jetzt zwischen
- lvalue (bleibt so wie vorher)
- xvalue (Objekt, referenziert durch eine unbenannte &&-Referenz)
- prvalue ("pure" rvalue, das sind alle rvalues außer xvalues)
Die ersten beiden fasst man zusammen als "glvalues" und die letzten beiden als "rvalues".
Ja ne is klar. 'Aufräumen' gut das du das gleich in Anführungszeichen geschrieben hast. Das klingt mehr nach 'Machen wir es noch komplizierter'
Heheh.
So schlimm ist es aber nicht. Man hat "rvalue" eigentlich nur aufgeteilt in "xvalue" und "prvalue". Ich sehe ein, dass es sinnvoll ist, den Dingen einen Namen zu geben. Einige Stellen im Standard unterscheiden nur zwischen glvalues/prvalues, zB wenn es um Polymorphie und dynamische Typen geht. Andere Stellen unterscheiden nur zwischen lvalue/rvalue, zB wenn es um das "Binden" von Referenzen geht. Statt Sonderregeln und Ausnahmen zu bringen, kann man jetzt einfach einen der neuen Begriffe benutzen. Ich hoffe, es ist klar, wie das gemeint ist. "xvalues" hat es vorher (C++98, C++03) einfach nicht gegeben. In C++0x gibt es sie aber und sie haben sowohl Eigenschaften von lvalues als auch Eigenschaften von rvalues. Und so kann man sich auch das x merken... x = Kreuzung oder eXpendable (entbehrlich im Sinne der Move-Semantik). Ich kann mir nur auf das 'gl' in "glvalues" keinen Reim machen...
-
Sehr geil finde ich aber auch, dass eine überarbeitete (noch nicht öffentlich verfügbare) Version von N3044 akzeptiert wurde [1]. Damit kann ein Compiler automatisch Move-Konstruktoren und Move-Zuweisungen unter gewissen Umständen erzeugen.
-
Ben04 schrieb:
Aber Concepts hätte ich schon gern gehabt. Oder zu mindest irgendetwas was den typename Foo::template Bar<typename Blub::Bla>-Quark leichter zu lesen macht.
Ich hätte gerne so etwas wie Concepts gehabt, was nicht ganz so kontrovers angesehen wird. Ich finde die Idee an sich super. Aber hast Du mal versucht, das Feature im Detail zu verstehen? Ich behaupte, dass es die wenigsten Leute aus dem Komitee richtig verstanden haben. Der Verantworliche der LWG (Howard Hinnant, LWG = library working group) hat sogar offen zugegeben, dass er sich nicht zutrauen würde, Concepts in die StdLib einzubauen und auf die Expertise von anderen angewiesen ist, weil es keinen stabilen Compiler gibt, der die aktuellste Version des Concepts-Proposals implementiert und mit dem man hätte testen können.
Dann lieber beim nächsten Mal...
-
krümelkacker schrieb:
Aber hast Du mal versucht, das Feature im Detail zu verstehen?
Ich kenne mich mit der Problematik aus. Man hat versucht zu viele Teilprobleme mit einem großen Hammer zu erschlagen und ist dabei gescheitert. Wenn man die Teile separat angegangen hätte, dann hätte man für viele Fortschritte erreicht. So hat man gar nichts erreicht und unter anderem dieser typename-Blödsinn lebt weiter.
Die älteren GCCs konnten ohne weiteres auch im Templatekontext ohne typename überleben, dann kam der Standard und hat dafür gesorgt, dass die Länge der Typnamen sich etwa verdreifacht haben ohne, dass es für den Programmier den geringsten Mehrwert gegeben hätte. So etwas nennen dann wohl einige Fortschritt.
Es gibt einfach so viele Stellen an denen der Standard einfach nur Müll ist. Ein paar davon werden nun auch aufgeräumt aber viele bleiben und die Vorstellung, dass das wieder etwa 15 Jahre dauern soll bis sich etwas ändern macht mich traurig.
-
Ben04 schrieb:
So hat man gar nichts erreicht und unter anderem dieser typename-Blödsinn lebt weiter.
Naja, Blödsinn ist das ja nicht. Und bei Concepts denke ich in erster Linie an das "constraining" von Templates und weniger daran, dass ich irgendwo typenames sparen kann. Wir haben ja auch noch auto und decltype.
Ben04 schrieb:
Die älteren GCCs konnten ohne weiteres auch im Templatekontext ohne typename überleben, dann kam der Standard und hat dafür gesorgt, dass die Länge der Typnamen sich etwa verdreifacht haben ohne, dass es für den Programmier den geringsten Mehrwert gegeben hätte. So etwas nennen dann wohl einige Fortschritt.
Der "Mehrwert" ist das Zwei-Phasen-Lookup, mit dem auch einige Fehler schon vor dem Instanziieren eines Templates gefunden werden können. Ich finde das eigentlich eine super Idee. Je eher/vollständiger ein Template überprüft werden kann, desto besser. Concepts geht ja in dieselbe Richtung. Ich habe aber mit dem alten pre-Standard-Verhalten, also ohne ZPL, keine Erfahrung und kann daher nicht sagen, ob das ZPL das Programmieren signifikant erleichtert hat oder nicht -- mal abgesehen davon, dass man hier und da ein typename benötigt.
Ben04 schrieb:
Es gibt einfach so viele Stellen an denen der Standard einfach nur Müll ist.
Nenne mal bitte ein paar Beispiele. Ich sehe jetzt nicht, wie man C++0x in der Zeit mit den Ressourcen, die zur Verfügung standen, hätte besser machen können.
Ben04 schrieb:
[...] und die Vorstellung, dass das wieder etwa 15 Jahre dauern soll bis sich etwas ändern macht mich traurig.
Der Plan ist zumindest ein kürzerer Entwicklungszyklus. Stroustrup will den Nachfolger von C++0x eigentlich noch in dieser Dekade sehen -- C++1y sozusagen.
-
http://gcc.gnu.org/projects/cxx0x.html
http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x
-
Der Grund warum man typename und template überhaupt braucht ist, damit Sachen wie 1.
A::A<A::B<A::C,A:: D<A::E> >
sizeof(A::X)
ohne Typinformationen geparst werden können. In der Essenz ist es genau das selbe Problem wie mit
A<B<C>>
Letzteres hat man gefixt in dem man von den Leuten verlangt, dass sie die Operatoren einfach mit ()-Klammern umgeben. Wenn man das einfach konsequent immer fordern würde, dann wären die typenames & templates in 1. auch überflüssig.
Dann bleiben noch Exoten wie 2. wo man tatsächlich auch dann nicht aus dem Kontext entscheiden kann, ob ein Typ oder eine Variable gemeint ist, weil beides gültig ist.
In der ersten Phase kann der Compiler aber eh nur eine Verwendung eines A::X einmal als Typ und einmal als Variable als Fehler ankreiden. Mehr kann er gar nicht mit der Information anfangen. (Eventuell braucht man die Information noch um export zu implementieren aber das wurde ja entfernt.) In den Situationen wie 2. ist es aber völlig egal was A::X ist, weil es in jedem Fall gültig ist und des Wegen in jedem Fall keine Fehlermeldung geben würde.
Du siehst man kann auch ein ZPL ohne typename & template machen.
ZPL findet nur Fehler die bei jeder Instanziierung eh gefunden worden wären. Das heißt es bringt nur etwas für Templates die nie instanziiert werden. Das ist ein Vorteil, aber einer den du mit der Luppe suchen musst.
Concepts hätten den typename&template-Quark wahrscheinlich bedeutungslos gemacht, da wahrscheinlich niemand mehr unconstrainted Parameter verwendet hätte.
krümelkacker schrieb:
Ben04 schrieb:
Es gibt einfach so viele Stellen an denen der Standard einfach nur Müll ist.
Nenne mal bitte ein paar Beispiele.
Siehe oben für ein erstes Beispiel.
Ein weiteres (umstrittenes) Beispiel sind C-Regeln für void*. Jeder der void* verwendet scheißt auf Typsicherheit an der Stelle und ist sich dessen vollends bewusst. Typcasts nerven also nur und bringen eh keine zusätzliche Sicherheit, weil der Programmier eh nur castet bis der Compiler das Maul hält. Man kann nun sagen, dass void* allgemein böse ist, dann soll man es aber bitte einfach ganz entfernen. Mit char* und ein paar zusätzlichen Casts kommt man auch ohne aus. Der aktuelle Weg ist nichts Halbes und nix Ganzes.
Dann wäre es noch sinnvoll zu erlauben private Methoden außerhalb der Klasse zu deklarieren. Diese sind ein Implementierungsdetails welches nicht benötigt werden um die Größe eines Objekts zu berechnen. Es gibt also keinen Grund warum man sie in den Header packen müssen soll.
Die neuen variadischen Parameter gefallen mir auch nicht. Warum nicht einfach
template<class T> void f(T...t){ // T ist ein std::tuple und t eine Instanz davon }
und std::tuple dann direkt in den Compiler einbauen.
krümelkacker schrieb:
Der Plan ist zumindest ein kürzerer Entwicklungszyklus. Stroustrup will den Nachfolger von C++0x eigentlich noch in dieser Dekade sehen -- C++1y sozusagen.
Ich glaube, das x in C++0x war anfangs auch keine Hex-Ziffer.
EDIT: Smiley entfernt
-
typename
löst potenzielle Mehrdeutigkeiten auf. Gerade in generischem Code sollten die nicht passieren, da sonst total verrückte Dinge passieren können (Multiplikation statt Zeigerdeklaration und solche Sachen). Das wird in 99% der Fälle dann nicht kompilieren, doch wenn es kompiliert...
Gerade bei generischem Code in C++ weiß man nichts über den Typ, man kann nur Constraints textuell definieren. Entweder muss man also zusätzliche Constraints hinschreiben, die die Verwendbarkeit der Bibliothek unnötig begrenzen, man verschluckt die Hinweise darauf einfach (ganz schlecht) oder man zwingt den generischen (Bibliotheks-)Programmierer, immer im Quelltext explizit zu sein. Warum man nun auch den Programmierer in offensichtlichen Fällen, in denen nur Typen gehen, dazu zwingt, hat Stroustrup mal vor langer Zeit zusammengefasst: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1994/N0578.pdf
Kurz gesagt: Um den Programmierer davon abzuhalten, darüber nachzudenken, ob er jetzt eintypename
schreiben muss oder nicht, er muss es einfach immer schreiben.
(Noch ein kleiner Hinweis:class
ist nicht geeignet als Schlüsselwort; was wäre, wenn der Typ eine Union ist? Das wäre eine unnötige künstliche Einschränkung).
Außerdem werdenauto
unddecltype
typename
seltener machen.Concepts/Constraints wären zwar toll, aber da versuchen sich schon einige Leute seit jetzt 2 Jahrzehnten dran und es wird immer noch nicht im Standard drin sein. Es scheint doch ein eher komplexes Thema zu sein.
Die neuen variadischen Parameter gefallen mir auch nicht. Warum nicht einfach
template<class T> void f(T...t){ // T ist ein std::tuple und t eine Instanz davon }
Dein Vorschlag für die variadischen Parameter wäre zumindest syntaktisch Mist. Schon mal ausprobiert, was passiert, wenn du folgende Funktion in einem heutigen Compiler definierst:
template <typename T> void f(T...) { }
Daneben bin ich mir nicht sicher, ob das das Problem überhaupt löst... Du kannst ja schlecht in einer Schleife Variablen unterschiedlichen Typs definieren, wirst also vermutlich auch auf Rekursion zurückgreifen müssen wie mit dem System im aktuellen Standard (und das vielleicht noch deutlich häufiger). Ich glaube, es gibt schon gute Gründe, warum man Variadic Templates so gelöst hat und nicht anders.
Exception specifications sind im Übrigen nicht rausgeflogen; sie sind nur "deprecated", so wie
auto_ptr
oder die automatische Konvertierung bei Zeichenkettenliteralen vonconst char*
nachchar*
. Und dafür hat man eine neue Art Exception specification eingeführt,noexcept
, für den Fall, das keine Ausnahme fliegen darf/soll. Für den normalen Programmierer sieht das auf den ersten Blick so aus, wie das bisherigethrow()
, und es gibt wohl im Kommittee eine Mehrheit, die auch noch eine ähnliche, nahezu gleiche Semantik bei fliegenden Ausnahmen beibehalten will (nämlich direkt nachterminate()
). Abernoexcept
wird man voraussichtlich viel häufiger in Code sehen alsthrow()
, denn man wird es für Move Semantics benötigen und kann es dazu verwenden, auf No-throw-Garantien zur Compilezeit in C++ zu testen. Zumindest, wenn der Blog-Eintrag von Dave Abrahams noch einigermaßen stimmt.
-
Ben04 schrieb:
Ein weiteres (umstrittenes) Beispiel sind C-Regeln für void*. Jeder der void* verwendet scheißt auf Typsicherheit an der Stelle und ist sich dessen vollends bewusst. Typcasts nerven also nur und bringen eh keine zusätzliche Sicherheit, weil der Programmier eh nur castet bis der Compiler das Maul hält. Man kann nun sagen, dass void* allgemein böse ist, dann soll man es aber bitte einfach ganz entfernen. Mit char* und ein paar zusätzlichen Casts kommt man auch ohne aus. Der aktuelle Weg ist nichts Halbes und nix Ganzes.
Das sieht aber sehr stark nach deiner persönlichen Meinung aus, verallgemeinern kannst du das so nicht. Kein vernünftiger Programmierer castet, bis der Compiler das Maul hält. Und Typcasts nerven sicher nicht, abgesehen davon hättest du sie bei
char*
erst recht, während zuvoid*
immerhin noch eine implizite Konvertierung existiert.Überhaupt finde ich nicht, dass
void*
unsinnig ist. Man schafft damit explizit einen typlosen Zeiger. Was brächte es, wenn die Leute diesen mit einem Dummy-Typen wiechar
assoziierten? Damit würde man nur sinnlose Operationen erlauben, wie zum Beispiel Dereferenzierung oder Zeigerarithmetik.Ben04 schrieb:
Dann wäre es noch sinnvoll zu erlauben private Methoden außerhalb der Klasse zu deklarieren. Diese sind ein Implementierungsdetails welches nicht benötigt werden um die Größe eines Objekts zu berechnen. Es gibt also keinen Grund warum man sie in den Header packen müssen soll.
Doch, es gibt einen guten Grund dafür. Denn ganz so einfach wäre die Alternative nicht. Eine (private) Memberfunktion hat einen ganz besonderen Status: Sie darf auf alle Member der Klasse zugreifen, ungeachtet des Zugriffsspezifizierers (Basisklassen ausser Acht gelassen). Ich denke nicht, dass es so eine gute Idee wäre, dieses Privileg allen zur Verfügung zu stellen und somit die Kapselung komplett über Bord zu werfen.
Ausserdem kannst du das gewünschte Verhalten in vielen Fällen mit globalen Funktionen erreichen, wenn du ein wenig flexibel bezüglich Syntax bist.
-
Leute denkt eure Argumente bitte durch, ehe ihr etwas schreibt. Ich diskutiere gerne über das Thema, aber nur mit Leuten die sich entweder auskennen oder bereit sind sich auf Argument außerhalb ihres Tellerrands einzulassen.
Old McDonald schrieb:
Kurz gesagt: Um den Programmierer davon abzuhalten, darüber nachzudenken, ob er jetzt ein typename schreiben muss oder nicht, er muss es einfach immer schreiben.
Hast du mal gelesen, was der schreibt? Ich zitiere mal:
Stroustrup schrieb:
Without it [= typename & template], syntax analysis of C++ would need to interact closely with semantic analysis – and would probably be impossible even in relatively simple cases.
Du redest hier darüber was der Programmier machen darf und Stroustrup redet über das was implementierbar ist. Das sind zwei völlig verschiedene Paar Schuhe.
Old McDonald schrieb:
typename löst potenzielle Mehrdeutigkeiten auf. Gerade in generischem Code sollten die nicht passieren, da sonst total verrückte Dinge passieren können (Multiplikation statt Zeigerdeklaration und solche Sachen). Das wird in 99% der Fälle dann nicht kompilieren, doch wenn es kompiliert...
Das ist Blödsinn und das hättest du selbst eingesehen, wenn du nur probiert hättest ein Beispiel zu finden.
Old McDonald schrieb:
Außerdem werden auto und decltype typename seltener machen.
Ich schätze so um die 20% bis 30% typenames weg in meiner aktuellen Anwendung. Leider aber auch nur die, welche am wenigsten stören. Aber ich will nicht klagen, es ist ein Schritt in die richtige Richtung.
Old McDonald schrieb:
Dein Vorschlag für die variadischen Parameter wäre zumindest syntaktisch Mist. Schon mal ausprobiert, was passiert, wenn du folgende Funktion in einem heutigen Compiler definierst:
a) Die Syntax ist das kleinste Problem
b) Wie wäre es wenn du auch wirklich das was ich geschrieben habe durch den Compiler jagen würdest?Old McDonald schrieb:
Ich glaube, es gibt schon gute Gründe, warum man Variadic Templates so gelöst hat und nicht anders.
Super Argumentation
Soll ich dich mal über die Gründe aufklären?
* Variadic Templates sind schon länger im Draft als tuple
* tuple kann man ohne nicht ohne Compilermagie definieren.
Im Klartext: Das ist so gewachsen und wurde seit dem nicht mehr überprüft.Semantisch gibt es keinen Unterschied zwischen einem "argument pack" und einem tuple. Dadurch, dass man sie aber unterscheidet kann man die tuple Helferlein nicht verwenden und ist wesentlich weniger flexibel.
Nexus schrieb:
Und Typcasts nerven sicher nicht, abgesehen davon hättest du sie bei
char*
erst recht,Wo bitte habe ich etwas anderes behauptet? Entweder man mutet den Leuten zu mit void* umzugehen zu könnnen, dann aber auch richtig oder man mutet ihnen es nicht zu und lässt es ganz sein. Alles dazwischen ist ein fauler Kompromiss der niemandem hilft und nur unnötige C Inkompatibilitäten schafft.
In normalem Code mit normalem Stil wird sowieso kein void* vorkommen. Also ist die Frage in 99% der Fälle irrelevant. Bei den restlichen 1% musst du eh Casten wie wild, da es einfach nicht anders geht. Da nervt das nur und macht den Code schwer zu lesen.
Nexus schrieb:
Denn ganz so einfach wäre die Alternative nicht.
Doch, jede nicht deklarierte Methode ist implizit privat, nicht virtual und hat internen Linkage.
Nexus schrieb:
Ich denke nicht, dass es so eine gute Idee wäre, dieses Privileg allen zur Verfügung zu stellen und somit die Kapselung komplett über Bord zu werfen.
Tust du nicht. Ich hab jetzt keinen Bock zu erklären warum das funktioniert aber versuch einfach mal in folgendem Beispiel das foo ohne Hacks wie void* Casts zu verändern. Du wirst sehen, dass das nicht geht.
class Bar{ int foo; };
Achtung: Zum Verändern von foo gehört auch, dass dein Code ausgeführt werden kann.
Nexus schrieb:
Ausserdem kannst du das gewünschte Verhalten in vielen Fällen mit globalen Funktionen erreichen, wenn du ein wenig flexibel bezüglich Syntax bist.
Leider nein, da die dann nicht friend sind und somit nicht auf die Interna zugreifen können. Wenn sie friend sind, dann müssen sie aber im Header deklariert werden und ich könnte gleich reguläre Methoden verwenden. Was man machen kann ist folgendes:
struct Foo; class Bar{ friend struct Foo; int i; }; //... struct Foo{ static void blub(Bar*b){ b->i = 3; } }
Ist aber nicht schön. (Und hat ein paar weitere Probleme, die ich nicht ausführen will.) Des Weiteren ist es krank von vornherein einen Workaround als best-practice anzugeben.
-
Ben04 schrieb:
Leute denkt eure Argumente bitte durch, ehe ihr etwas schreibt.
Komm' mal von Deinem hohen Ross runter. So toll finde ich das jetzt auch nicht, was Du da von Dir gibst.
Ben04 schrieb:
Ich diskutiere gerne über das Thema, aber nur mit Leuten die sich entweder auskennen oder bereit sind sich auf Argument außerhalb ihres Tellerrands einzulassen.
Relax. Da gibt's nen tolles Sprichwort: Surround yourself with smart people who disagree with you.
Ben04 schrieb:
[...] Soll ich dich mal über die Gründe aufklären? [...]
Das grenzt an eine Beleidigung. Das muss doch nicht sein.
Ben04 schrieb:
[...] Ich hab jetzt keinen Bock zu erklären warum das funktioniert aber [...]
Mit anderen Worten: Du bist nicht ernsthaft an einer Diskussion interessiert.
Wenn Du so tolle Ideen hast, dann kannst Du ja mal probieren, sie unmissverständlich und detailliert aufzuschreiben und sie als Proposal für C++1y einzureichen. Vergiss die Beispiele zum Verdeutlichen nicht. So gesehen könnte das hier eine kleine Übung für Dich sein.
-
Ben04 schrieb:
Die neuen variadischen Parameter gefallen mir auch nicht. Warum nicht einfach
template<class T> void f(T...t){ // T ist ein std::tuple und t eine Instanz davon }
Wo soll denn das tuple-Objekt herkommen? Und wie lässt sich das mit perfect forwarding vereinbaren? Ist das wirklich "einfacher"? Zeig mal Beispiele, bei denen man sehen kann, dass Dein Ansatz "besser" ist. Es ist doch eigentlich Deine Aufgabe, überzeugende Beispiele zu suchen und nicht meine. Ich sehe jetzt auf Anhieb keinen klaren Vorteil, eher Probleme -- und Gedanken lesen kann ich auch nicht.
Das trifft auch auf einiges anderes zu, was Du angesprochen hast. Die Wahrheit ist, dass es für mich so aussieht, als wären Deine Vorschläge nicht zuende gedacht. Das musst Du jetzt nicht persönlich nehmen. Es ist nur mein erster Eindruck. Wenn Du dich jetzt querstellst, Deine Sachen weiter zu erläutern, dann können wir's auch gleich sein lassen.
-
krümelkacker schrieb:
Ben04 schrieb:
[...] Soll ich dich mal über die Gründe aufklären? [...]
Das grenzt an eine Beleidigung. Das muss doch nicht sein.
Ich empfinde es auch beleidigen wenn dein gegenüber deine Argumente einfach ignoriert oder gar behauptest du hättest etwas gesagt obwohl klipp und klar etwas anders da steht.
krümelkacker schrieb:
Ben04 schrieb:
[...] Ich hab jetzt keinen Bock zu erklären warum das funktioniert aber [...]
Mit anderen Worten: Du bist nicht ernsthaft an einer Diskussion interessiert.
Eventuell ist es auch jedem klar der es überhaupt mal versucht hat. Du hast das offensichtlich nicht und deswegen macht es auch keinen Sinn meine Zeit damit zu verschwenden dir etwas zu erklären.
krümelkacker schrieb:
Wo soll denn das tuple-Objekt herkommen?
Das ist so ein Beispiel von nicht lesen können.
krümelkacker schrieb:
Es ist doch eigentlich Deine Aufgabe, überzeugende Beispiele zu suchen und nicht meine.
Wie bitte? Ich muss rein gar nichts. Du bist derjenige der die erste Frage gestellt hat.
-
Ben04 schrieb:
krümelkacker schrieb:
Es ist doch eigentlich Deine Aufgabe, überzeugende Beispiele zu suchen und nicht meine.
Wie bitte? Ich muss rein gar nichts. Du bist derjenige der die erste Frage gestellt hat.
Ich bin derjenige, der wissen wollte, was an Deiner Variation von variadischen Templates besser/toll/eleganter sein soll. Das erschließt sich mir nicht automatisch. Ich weiß nicht einmal wie Du das genau gemeint hast. Und das liegt sicherlich nicht allein an mir. Ein "X ist doof, Y ist besser" ist ohne Begründung irgendwie wertlos, findest Du nicht? Habe ich da etwas übersehen?
<nochmal-nachguck>
Nein. Ich habe nichts übersehen. Alles, was Du dazu gesagt hast, war
Warum nicht einfach
template<class T> void f(T...t){ // T ist ein std::tuple und t eine Instanz davon }
und std::tuple dann direkt in den Compiler einbauen.
Das wars. Nur halb erklärt, was das bedeuten könnte, und keine weiteren Argumente oder Beispiele. Wie soll ein Aufruf aussehen? Wo wird das Tuple erzeugt? Muss ich das manuell machen? Soll es automatisch passieren? Wenn ja, wo? Beim Aufrufer oder innerhalb der Funktion? Welchen Typ hat denn eine bestimmte Instanz des Funktions-Temlates?
void(int,double)
odervoid(tuple<int,double>)
? Falls letzteres, würd ich behaupten, dass es eine schlechte Idee wäre. Es wären dann ja nicht wirklich variadische Funktionen sondern lediglich Syntax-Zucker bei dem Tuples auf der Aufruferseite automatisch erzeugt werden. Zeig mal ein Beispiel mit "perfect forwarding". Und warum will man das jetzt nochmal so schreiben? wo sind denn die Vorteile?Sorry, ohne weitere Erklärungen sieht das eher nach einem Gedankenfurz eines Großmauls aus. Ich habe schon den Eindruck, dass Du Dich mit C++ auskennst. Das impliziert aber keine guten Kommunikations-Skills. Du magst Dir ja dabei was gedacht haben ... aber davon ist leider nichts angekommen.
-
Ben04, es ist ziemlich traurig, was du hier abziehst. Old McDonald, krümelkacker und ich haben uns Mühe gegeben, sich Argumente zu überlegen und eine sinnvolle Diskussion zustande zu bringen. Dann kommst du und tust so, als wären wir Vollidioten, mit denen so eine Diskussion reine Zeitverschwendung wäre. Selbst bist du natürlich der grosse Macker. Sehr guter Diskussionsstil, wirklich.
Falls du wieder bereit bist, eine normale Konversation zu führen, sag das. Dann erkläre ich auch, was ich mit meinen Punkten meinte und wieso es tatsächlich nicht so einfach ist. Aber auf sowas hier hab ich echt keine Lust.
-
krümelkacker schrieb:
Das wars. Nur halb erklärt, was das bedeuten könnte, und keine weiteren Argumente oder Beispiele. Wie soll ein Aufruf aussehen?
Das tuple-Interface ist doch klar, oder? In C++98 lässt sich das nicht definieren, also muss die tuple-Klasse halt direkt in den Compiler gebaut werden.
Dann macht der Compiler aus:
printf("...", a, b+1, move(c));
kurz
printf("...", make_tuple<int&, const int&, int&&>(a, b, c));
und man ist fertig. Die Vorteile sind, dass du mit dem Tupleparameter alles machen kannst was man mit Tuplen machen kann. Zum Beispiel kann man ein Objekt auch hinten abpellen anstatt immer nur vorne. Du kannst die Tuple-Objekte kopieren und zwischen speichern. Natürlich geht das auch mit Argumentpacks, diese sind ja schließlich von der Natur her genau das selbe und beide lassen sich ineinander überführen. Syntaktisch sind sie aber so verschieden, dass man die Tuple-Helferfunktionen nicht drauf anwenden kann. Warum also beides zweimal reinpacken? Das führt dazu, dass ich die Helferfunktionen zweimal schreiben muss oder ständlich ein make_tuple(blub...) mache. Ich hoffe ich brauche nicht erklären warum es schlecht ist ein und das selbe Feature (Tuples und Argumentpacks) zweimal mit unterschiedlichem Syntax zu haben.
[EDIT]Noch eine Sache die mit Tupel einfach ist aber mit Argumentpacks sehr schwer: Die Anzahl der Elemente soll ein perfektes Quadrat sein. Das "braucht" man zum Beispiel bei quadratischen Matrizen. Bei tuples liegt die Information über die Anzahl der Parameter als constexpr vor und kann direkt überprüft werden. Bei Argumentpacks muss ich mir die erst zusammenbauen (geht natürlich auch).[/EDIT]
krümelkacker schrieb:
Welchen Typ hat denn eine bestimmte Instanz des Funktions-Temlates?
void(int,double)
odervoid(tuple<int,double>)
? Falls letzteres, würd ich behaupten, dass es eine schlechte Idee wäre. Es wären dann ja nicht wirklich variadische Funktionen sondern lediglich Syntax-Zucker bei dem Tuples auf der Aufruferseite automatisch erzeugt werden.Siehst du, dass du auch von selbst drauf kommst, wenn du es nur versuchst. Wenn du noch mal nachdenkst, dann kommst du auch, darauf, dass variadische Templates nicht "variadischer" sind, sondern bis auf den Syntax genau das selbe machen.
Und jetzt mal ehrlich: Hast du bei der private-Geschichte überhaupt versucht ein Beispiel zu basteln, das die Kapselung bricht? Wenn nicht dann ist mein Gebashe gerechtfertigt.
-
Ben04 schrieb:
Die neuen variadischen Parameter gefallen mir auch nicht. Warum nicht einfach
template<class T> void f(T...t){ // T ist ein std::tuple und t eine Instanz davon }
und std::tuple dann direkt in den Compiler einbauen.
Frage:
Wie machst du dann sowas:template<typename> class function; template<typename R, typename ...ArgTs> class function<R(ArgTs...)> { // ... };
Auch wird zum Beispiel Boost.Spirit sicher ähnliches verwenden für die Regeln oder
std::bind
usw. Es geht hier nicht nur um eine variable Anzahl Parameter bei einer Funktion sondern auch Typen, welche durch eine variable Anzahl anderer Typen definiert werden. Nicht nurstd::tuple
macht davon Verwendung. Du unterschätzt hier eindeutig das Einsatzgebiet von Variadic Templates.Zu dem template&typename Kram, da stimme ich dir im Grossen und Ganzen zu. Es haben schon verschiedene Hersteller gezeigt, dass man es auch mit deutlich weniger umsetzen kann.
Bei
void*
ist es meiner Meinung nach eher eine subjektive Sache. Wenn ichvoid*
verwende, dann scheisse ich nicht auf die Typsicherheit, sondern hole diese meistens anderweitig nach. Ein kleinerstatic_cast
schadet da nicht und verhindert irgendwelche impliziten Unachtsamkeiten. Sicherlich nur ein sehr kleiner Schutz, aber mich stört dieserstatic_cast
wirklich nicht. Im Gegenteil, man könnte argumentieren, dass man damit den Leser darauf aufmerksam macht, dass man hier wieder in die normale Typsicherheit zurückwechselt. Der umgekehrte Fall ist schliesslich nicht so wichtig, da man mitvoid*
selber nichts anfangen kann.Grüssli
PS: Missverständnisse und andere Meinungen gibt es immer in Diskussionen. Deswegen ist der Diskussionpartner aber nicht gleich dumm und man muss ihn sicherlich nicht von oben herab behandeln.
-
Sieht doch schon besser aus.
Ben04 schrieb:
Und jetzt mal ehrlich: Hast du bei der private-Geschichte überhaupt versucht ein Beispiel zu basteln, das die Kapselung bricht? Wenn nicht dann ist mein Gebashe gerechtfertigt.
Ja. Wie stellst du sicher, dass der Anwender nicht in seinem Code Funktionen definiert, diese als privat bezeichnet und somit vollständigen Zugriff auf Klasseninterna erhält?
Der Entwickler der Klasse sollte eine Möglichkeit haben, zu bestimmen, wer welchen Zugriff erhält. Und dafür scheint mir die Klassendefinition am geeignetsten.