Kopie oder konstante Referenz?
-
Hallo,
momentan programmiere ich eine Mathematik-Lib, in der Polynome als Vektorräume aufgefasst werden, wobei die Koeffizienten in einem std::array/std::vector (letzteres bei Rang 0 -> dynamische Dimension) gespeichert werden.
Nun habe ich folgende Funktion, um das Polynom auf eine Variable anzuwenden (im Fall Rang > 0):
template <typename N,size_t D> class Polynomial : public VectorSpace<N,D+1> { // stuff template <typename T> T calc(T t); // stuff }Mein Problem ist, dass T sowohl ein Basistyp als auch ein benutzerdefinierter Typ (z.B. ein Vektor) sein kann.
- Gibt es eine Möglichkeit, den Compiler entscheiden zu lassen, ob er als Argument eine Kopie oder eine konstante Referenz nimmt?
- Falls dies nicht möglich sein sollte, wäre es dann klüger, konstante Referenzen zu nutzen?
MfG
-
du könntest deine funktion für die standard datentypen spezialisieren, das könnte nur sehr viel redundanz und schreibarbeit nach sich ziehen
ansonsten lieber const&
-
- Theoretisch ja. Mit Templatemetaprogrammierungsmonstern kannst du feststellen, ob es ein eigebauter oder ein benutzerdefinierter Typ ist und entsprechend Spezialisierungen einführen. Fertige Templatemetaprogrammierungsmonster gibts in der Boost-Bibliothek und ab C++11 auch in der Standardbiblitohek.
- Mach das. Der mögliche Effizienzverlust ist sehr, sehr gering. Da Templates naturgemäß für den Compiler oft voll transparent sind, wird eventuell sogar alles perfekt optimiert, so dass es gar keinen Unterschied gibt.
-
Skym0sh0 schrieb:
du könntest deine funktion für die standard datentypen spezialisieren, das könnte nur sehr viel redundanz und schreibarbeit nach sich ziehen
Das wären extrem viele Spezialisierungen, das widerstrebt irgendwie dem Prinzip der Templates.
Skym0sh0 schrieb:
ansonsten lieber const&
Bringt es eigentlich wirklich so viel, Standardtypen und Iteratoren als Kopie anstatt als Referenz zu übergeben? Was daran effizienter sein soll, habe ich nie wirklich verstanden.
-
Du könntest auch was mit enable_if und is_fundamental basteln.
Edit: Ach, das hat der Sepp ja quasi schon geschrieben.
-
Polynomteiler schrieb:
Das wären extrem viele Spezialisierungen, das widerstrebt irgendwie dem Prinzip der Templates.
Och, es geht.
template <typename T> struct identity { typedef T type; }; template <typename T, bool B> struct val_or_cref : identity<T> {}; template <typename T> struct val_or_cref<T, false> : identity<T const&> {}; template <typename T> struct per_value_candidate : std::integral_constant<bool, std::is_pod<T>::value && sizeof(T)<=sizeof(void*)> {}; template <typename T, bool PerValue = per_value_candidate<T>::value> T calc(typename val_or_cref<T, PerValue>::type t) { std::cout << (std::is_reference<typename val_or_cref<T, PerValue>::type>::value ? "per const-ref" : "per value") << '\n'; return t; } int main() { calc<int>(4); calc<std::string>("x"); calc<int*>(0); }Mich stört eher, dass T nicht mehr deduziert werden kann. Deshalb würde ich das niemals so machen und mich auf den Compiler verlassen.
-
Tachyon schrieb:
Du könntest auch was mit enable_if und is_fundamental basteln.
Edit: Ach, das hat der Sepp ja quasi schon geschrieben.
Jetzt, wo du es erwähnst, fällt mir ein, dass ich mal ein
Select<bool,typename,typename>geschrieben habe.
Daran hätte ich mal eher denken sollen, danke
-
Polynomteiler schrieb:
Bringt es eigentlich wirklich so viel, Standardtypen und Iteratoren als Kopie anstatt als Referenz zu übergeben? Was daran effizienter sein soll, habe ich nie wirklich verstanden.
Theoretisch bringt es sehr, sehr viel. Kannst dir ja mal den Unterschied überlegen zwischen einem Wert in einem Register und einem Zeiger auf den Wert. Praktisch wird's aber eh wegoptimiert.
-
cooky451 schrieb:
Praktisch wird's aber eh wegoptimiert.
Nur wenn die Funktion in der gleichen UE definiert wird. Das ist bei Templates zufällig der Fall.
-
Und wenn nicht könnte auch LTO greifen. Ansonsten haben Zeiger und Referenzen ohne Optimierung natürlich eine Indirektion mehr als eine Kopie, deren Auflösung Zeit kostet.
-
Ich werf mal einenspontanen Vorschlag in den Ring, mal gucken wer ihn zerreißt:
template <typename T> T calc(T&& t); //C++11 "universal reference"
-
praktiker schrieb:
Nur wenn die Funktion in der gleichen UE definiert wird. Das ist bei Templates zufällig der Fall.
Ich nehme Compiler ohne Link-Time-Optimization eh nicht mehr ernst.

-
-
Und verwende bitte nicht den Begriff "konstante Referenz". Warum, erfährst du hier.
-
cooky451 schrieb:
Polynomteiler schrieb:
Bringt es eigentlich wirklich so viel, Standardtypen und Iteratoren als Kopie anstatt als Referenz zu übergeben? Was daran effizienter sein soll, habe ich nie wirklich verstanden.
Theoretisch bringt es sehr, sehr viel. Kannst dir ja mal den Unterschied überlegen zwischen einem Wert in einem Register und einem Zeiger auf den Wert.
Das. Außerdem ist ein per Value übergebenes Argument garantiert aliasfrei.
pumuckl schrieb:
Ich werf mal einenspontanen Vorschlag in den Ring, mal gucken wer ihn zerreißt:
template <typename T> T calc(T&& t); //C++11 "universal reference"Das ist immer noch eine Referenz.
-
camper schrieb:
pumuckl schrieb:
Ich werf mal einenspontanen Vorschlag in den Ring, mal gucken wer ihn zerreißt:
template <typename T> T calc(T&& t); //C++11 "universal reference"Das ist immer noch eine Referenz.
Nicht nur das, sie ist auch nicht mal const, man kann also das Objekt darüber verändern, das bringt's irgendwie so gar nicht?