Sprachmittel, die ihr nie benutzt
-
Was Memberzeiger angeht:
template<typename T, typename D, typename F = std::plus<D> > class member_accumulation_functor { public: inline explicit member_accumulation_functor(D T::*p, F const &f = F()) : ptr_(p), func_(f) {} inline D operator()(D const &prev, T const &x) { return func_(prev, x.*ptr_); } private: D T::*ptr_; F func_; }; template<typename T, typename D, typename F> inline member_accumulation_functor<T, D, F> member_accumulator(D T::*p, F const &f) { return member_accumulation_functor<T, D, F>(p, f); } template<typename T, typename D> inline member_accumulation_functor<T, D> member_accumulator(D T::*p) { return member_accumulation_functor<T, D>(p); } ... struct foo { double x, y, z; } foo_array[10]; double sum = std::accumulate(foo_array, foo_array + 10, member_accumulator(&foo::y));Jetzt kann man natürlich argumentieren, dass das sauberer mit get-Methoden x(), y() und z() gelöst wäre, aber mal unter uns Pastorentöchtern, im namespace { } benutzt man der Schnelle halber halt doch mal nackte Structs, und gelegentlich hat man ja auch mit C-APIs oder generell nackten Structs anderer Leute zu tun. Es ist nicht besonders häufig, dass man so etwas braucht, aber gelegentlich doch ganz praktisch zu haben.
Was ich nicht (mehr) einsetze, weil es meiner Erfahrung nach unnötig Flexibilität entfernt, sind Templates als Template-Argumente, etwa
template<typename T, template<typename> class U> class foo { U<T> bar; }; foo<int, my_template> baz;Das ist meiner Meinung nach besser als
template<typename T> class foo { T bar; } foo<my_template<int> > baz;geschrieben. Kommt auch selten als Pflichtparameter vor, und die Standardbibliothek hält es beispielsweise mit Vergleichsfunktionen ähnlich.
-
camper schrieb:
Wäre das nicht einfach durch eine entsprechende virtuelle Funktion zu lösen?
Kann man machen. Aber das is thalt nur ein einziger mathematischer Sonderfall (der recht wichtig ist). Und da wollen wir eigentlich kein abstraktes Interface mit zu müllen. Es gibt zwar mehr Sonderfälle, aber jede braucht ihre eigene handgedengelte Lösung.
-
@ seldon: Ich verwende Template-Template-Parameter hauptsächlich bei Policy-Based-Design. Und da ich die Problemstellung des Akkumulators in der Form recht selten habe, hätte ich wohl eine guten alte Schleife genommen

@ otze: Oder du nimmst einen Dispatcher, der irgendeine Typ-/Kategorie-ID auf Funktionen abbildet. Prinzipiell eine
std::map<std::type_info, std::function<void(Base*)>, comp>, eventuell auch was Anderes alstype_info. Ist besonders praktisch, um virtuelle Funktionen in Klassen zu simulieren, deren Interface man nicht antasten kann/will. Ich hätte auch eine fertige Implementierung dazu.typeidhat allerdings zwei Probleme: Vergleiche (und dadurch dispatchte Aufrufe) sind recht langsam.dynamic_castallerdings auch. Ausserdem erkennst du keine Derived-To-Base-Konvertierungen, eine vonOptimizedDerivedabgeleitete Klasse müsste also separat beim Dispatcher registriert werden. Von daher könnte sich unter Umständen eine FunktionBase::GetID()bewähren, falls du mehrere Klassen gleich behandeln willst.Doch ob sich das in deinem Fall lohnt, kann ich nicht sagen. Virtuelle Funktionen wären wohl das Naheliegendste. Aber ab und zu sind unkonventionelle Lösungen ganz nützlich.
-
SeppJ schrieb:
Hier auch mal mein Senf, falls es jemandem interessiert:
Sprachmittel, die ich nie einsetze:- Exception-Spezifikationen true
- variable Argumentlisten Nur in Makros, ansonsten variadic templates

- Speicherklassen-Schlüsselwörter
registerundauto(in seiner alten Bedeutung) true - Pointer-to-Member (nicht Memberfunktionszeiger) (nur mal versehentlich, als ich einem Compilerfehler begegnet bin) wow, kannte ich gar nicht
- Function-Style- und C-Casts true, was ist eigentlich der Unterschied zwischen einem Function-Style-Cast und einem Konstruktoraufruf?
- Bitfelder true
- Digraphen, Trigraphen true
exporttrue- eigene
new- unddelete-Operatoren true, außer zum Debuggen, bevor ich smart-Pointer entdeckt habe dynamic_casttrue, außer in altem Code mit schlechtem Designuniontruegoto, Labels truevolatiletrue, außer bei Mikroprozessor- und evtl. Multithread-Programmen (welche ich im Moment nicht schreibe)- Inline-Assembler
wchar_t, wide Strings & Co. verwende ich durchaus, allerdings nicht mit wchar_t, da wchar_t manchmal nur 2 Byte breit ist und das dann nur halbes Unicode wäre- Lokale Klassen (d.h. Klassen in Funktionen, nicht Klassen in Klassen) true
operator->*,operator~sehr spezifisch, ~ z.B. für Bit-Frickling auf dem Mikroprozessor, -> selbst überladen nicht aber für den Aufruf von Memberfunktionen erst gestern verwendet (hast duoperator.*vergessen?)*- Überladung von:
operator->*,operator&,operator~,operator+(unär), logische Verknüpfungsoperatoren. true - Präprozessordirektiven: #line, #error, Nulldirektive
, im Standard vordefinierte Makros (die sind total nutzlos!)
#line und Nulldirektive kannte ich gar nicht (Nulldirektive zumindest nicht als solchese Wort, nur als "Designzeichen"); #error für Plattform-Fehler; __FILE__, __LINE__, __DATE__ und __TIME__ evtl. zu Debuggingzwecken
Sehr selten:- virtuelle Vererbung true + protected-/private-Vererbung
- Funktionstemplate-Spezialisierung momentan nicht
const_casttrue- Arrays true, dazu gibts ja (seit kurzem/bald, je nach Compiler) std::array
switchJa, kaum zu glauben, aber es kommt einfach nie vor, außer bei Hausaufgaben im Forum. truemutabletrue, kannte ich noch gar nichtexplicitAuch kaum zu glauben. Aber meine Konstruktoren haben irgendwie immer entweder 2+-Argumente oder sollen tatsächlich implizit sein. true- float, short, signed char, long double true
continueselten- Überladung von:
operator,,operator-(unär) Komma-Operator nicht, unäres Minus schon (was spricht dagegen?)
Auch sehr selten bis gar nicht: RTTI
-
wxSkip schrieb:
virtuelle Vererbung true + protected-/private-Vererbung
Oh, die habe ich noch vergessen. Fallen unter nie.
[*]Überladung von:
operator,,operator-(unär) Komma-Operator nicht, unäres Minus schon (was spricht dagegen?)Es spricht (im Gegensatz zum Komma) nichts dagegen, unäres Minus zu überladen, ich brauchs nur nie.
-
SeppJ schrieb:
Es spricht (im Gegensatz zum Komma) nichts dagegen, unäres Minus zu überladen, ich brauchs nur nie.
Aber operator* und operator/ brauchst du?
-
wxSkip schrieb:
Aber operator* und operator/ brauchst du?
Benutzen: klar. Überladen: Ja, auch. Iteratorenklassen, mathematische Klassen. Kommt nicht oft vor, aber auch nicht "fast nie". Und solche Klassen aktiv benutzen kommt dauernd vor, das würde ich auch zählen.
-
SeppJ schrieb:
Iteratorenklassen, mathematische Klassen.
Bei Iteratorenklassen meinst du wohl den unären?
Bei mathematischen Klassen kann man zwar a * -1 oder 0 - a schreiben, aber dort könnte man doch trotzdem den unären Minus-Operator überladen, oder?
-
SeppJ schrieb:
wxSkip schrieb:
virtuelle Vererbung true + protected-/private-Vererbung
Oh, die habe ich noch vergessen. Fallen unter nie.
Seit
boost::noncopyableverwende ich die private Vererbung sehr häufig.
-
wxSkip schrieb:
SeppJ schrieb:
Iteratorenklassen, mathematische Klassen.
Bei Iteratorenklassen meinst du wohl den unären?
Ja.
Bei mathematischen Klassen kann man zwar a * -1 oder 0 - a schreiben, aber dort könnte man doch trotzdem den unären Minus-Operator überladen, oder?
Ja, könte man. Ich streite auch gar nicht ab, dass das nützlich sein kann. Ich brauch's bloß irgendwie fast nie. Denk dran, dass ich bei mir unter fast nie auch Mittel wie switch gepackt habe. Vielleicht programmiere ich einfach anders.
-
Kann mir mal jemand erklären, was das für ein Sprachmittel sein soll?
http://cdecl.ridiculousfish.com/?q=(double+(^)(int+%2C+long+long+))foo
Das habe ich noch nie gesehen, geschweige denn benutzt.
-
Soweit ich weiss, handelt es sich um eine Spracherweiterung (C++/CLI), wo ^ vom GC verwaltete Zeiger darstellt.
-
Nicht ganz. Das ist eine eigenen Sprache von Microsoft, auch Managed C++ genannt. Quasi C++ mit .NET.
-
314159265358979 schrieb:
Nicht ganz. Das ist eine eigenen Sprache von Microsoft, auch Managed C++ genannt. Quasi C++ mit .NET.
C++/CLI ist der Nachfolger von Managed C++ und ist C++ mit .Net.
Weder Managed C++ noch C++/CLI sind neue Sprachen, beide bauen auf Erweiterungen auf. Managed C++ hat dabei ausschliesslich Kompilererweiterungen verwendet, also nach dem C++ Standard legale Erweiterungen. Ich bin mir nicht ganz sicher, ob alles legal war, aber deutlich mehr als bei C++/CLI. C++/CLI verwendet dagegen illegale Erweiterungen (z.B. bei den Schlüsselwörtern), dafür muss man nicht überal __ (doppelter Unterstrich) hinschreiben und der Code ist deshalb leserlicher.Grüssli
-
Okay, so genau weiß ich das auch nicht

-
prive schrieb:
Seit
boost::noncopyableverwende ich die private Vererbung sehr häufig.Kannste doch auch public vererben. Lohnt sich halt nur nicht der Aufwand, das "public" hinzuschreiben.
Gibt's denn irgendwelche Idiome, wo private/protected-Vererbung verwendet wird?
-
Siehe z.B. Effective C++.
private -> "is implemented in terms of".
protected -> Weiß er selbst nix dazu.
-
314159265358979 schrieb:
protected -> Weiß er selbst nix dazu.
Er weiß, aber verrät es Dir nicht.
-
private-Vererbung macht mitunter Sinn, um die Fähigkeiten der Basisklasse nutzen zu können ohne dafür eine ist-ein Beziehung darzustellen (das betrifft allerdings nur Grenzfälle, wo virtuelle Methoden oder protected-Methoden der Hilfsklasse eine Rolle spielen). protected-Vererbung ist imho unnötig.
@Sepp: Wenn du schon eine Zahlen-Klasse definierst, sollte die auch alle Operatoren unterstützen, die von int bzw. double bekannt sind - inklusive der unären Vorzeichen-Operatoren.
-
314159265358979 schrieb:
private -> "is implemented in terms of".
In der C++ FAQ lite steht's als Quasi-Komposition. Das mit "is implemented in terms of" hab ich auch schon mal gelesen. Widersprechen sich diese beiden nicht?
So oder so verrät das (mir) das aber noch keinen sinnvollen Einsatzzweck. Obwohl, irgendeiner wird sich wohl konstruieren lassen.