001
07.10.2005, 08:48 Uhr
virtual
Sexiest Bit alive (Operator)
|
Zitat von Pablo: |
1. Ich hab folgendes:
C++: |
... for(std::vector<T>::iterator pix = this->pixels.begin(); pix != this->pixels.end(); ++pix) ...
|
so bekomme ich folgende Warnungen (g++ 3.3.6):
Code: |
bild.hh: In member function `void bild<T>::printImage()': bild.hh:56: warning: `std::vector<T, std::allocator<_CharT> >::iterator' is implicitly a typename bild.hh:56: warning: implicit typename is deprecated, please see the documentation for details
|
Wenn ich typename hinzufüge
C++: |
for(typename std::vector<T>::iterator pix = this->pixels.begin(); ...
|
dann verschwinden die Warnungen. Wieso? Das verstehe ich nicht, warum da von implizite typenames geredet wird. Worauf bezieht sich das typename in der for Schleife? ihmo. wäre es schon klar, dass T ein typename ist, da ich davor template<typename T> habe, oder wie soll ich das verstehen?
|
Genauer gesagt geht es um den template lokalen Typen iterator: " std::vector<T>::iterator" ist ja ein Typ und das typename davor wird (neuerdings) vom Standard gefordert um den Compiler darin zu unterstützen, daß er leicht erkennt, daß es ein Typname ist. Es sind mit Templates konstruktionen denkbar, wo der Compiler dies nicht erkennen kann, deshalb gilt in C++ die Regel, daß wenn man außerhalb eines Templates einen Typen referenziert, welche lokal zum Templat definiert wurde, mit typename "hervorheben" muß.
Zitat von Pablo: |
2. Bei C wäre es eigentlich egal, ob ich (wie z.b. in der obigen for Schleife) ++pix oder pix++ hätte, doch ich habe hier mehrmals gelesen, dass bei C++ (besonders mit iteratoren oder so) die Stelle des "++" wichtig wäre, an dieser Stelle müsste eigentlich ++pix stehen und nicht pix++, wieso? Oder spielt das nur ein Rolle, wenn der Operator++ überladen wird?
|
Ja, im Prinzip spielt es nur dann eine Rolle, wenn op++ überschrieben ist. Aber die deutlich schwerer beantwortbare Frage ist: weisst Du es denn immer, ob op++ überschrieben wurde? Ich persönlich ziehe ++i generelle dem i++ vor (wenn ich die Wahl habe natürlich), weil man bei ++i eigentlich immer auf der richtigen Seite ist. Vgl. FAQ.
Zitat: |
3. was macht die "Spezialisierung"? Ich meine (das habe ich gelesen), wenn ich hab
C++: |
//allgemeine Version template<typename T, int size> void MyVector::multWith(double a) { for(int i=0;i<size;i++) { data[i]*=a; } }
|
Das wäre ein skalare Multiplikation für MyVector, sie läuft ganz gut, hat aber eine for-schleife. Dann kann ich auch sowas machen:
C++: |
template<> void MyVector<float,3>::multWith(double a) { data[0]*=a; data[1]*=a; data[2]*=a; }
|
was schneller und ohne Schleife laufen soll. Wieso? was sind die Vorteile da? Was ist, wenn ist 1000 statt 3 habe? Dann wäre eine Mordarbeit alle data[1...999] zu schreiben. Oder hab ich da was falsch verstanden?
|
Spezialisierung kann man aus verschiedenen Gründen machen. Ein Grund kann sein, daß man für einen Datentypen eine schnellere implementierung finden kann, ein anderer, daß man für ein (geringfüfig) anderes Verhalten erzwingen möchte. Bei obigen Beispiel will der Auto möglicherweise den zusätzlichen Overhead, den die Schleife mit sich bringt umgehen und geht bei kleinen Werten für den Templateparameter size (hier 3) hin und schreibst explizit hin... In 99% der Fälle würde ich solche Spezialisierungen ablehnen, weil so ein Loopunrolling ohnehin oft schon vom Optimierer gemacht wird. Ein anderes Beispiel wäre zB:
C++: |
template<typename T> bool is_equal_to(const T& a, const T& b) { return a==b; }
|
Dieses Template geht halt hin und vergleicht zwei Werte, ob sie gleich sind. Okay, soweit klar. Nun macht man aber bei Fließkommazahlen oft die Erfahrung, daß manche Zahlen gleicher sind als andere. Hier würde man vielleicht schreiben:
C++: |
template<> bool is_equal_to(const double& a, const double& b) { return std::fabs(a-b)<=2*std::numeric_limits<double>::epsilon(); }
|
Um den Rundungsproblemen aus den Weg zu gehen.
Zitat: |
4. Sind Iteratoren, wie std::vector<int>::iterator wirklich nur ein Pointer auf ein Element von vector?
|
Ein Klares nein: bei std::map, std::list usw hast Du in der Regel kompliziertere Vectoren, auch vector Iteratoren müssen nicht zwingend einfach nur ein Zeiger sein.
Zitat: |
5. Vererben die abgeleiteten Klassen auch die Konstruktoren, oder muss man neue schreiben, immer wenn man eine Klasse ableitet?
|
Man muß neue schreiben, Ausnahmen sind der Defaultconstructor und der Copyconstructor. -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) |