std::size (analog zu begin/end)
-
Wie wäre es mit
std::size?Das wäre doch mal was. Auch eine allgemeinere Form von
.size()für Container odersizeoffür Arrays.Sollte doch eigentlich auch dabei sein. (Oder ich übersehe gerade etwas*; die Idee ist mir vor etwa einer halben Minute in den Kopf gekommen)
~* Ja, ich glaube das braucht einfach kein Schwein...~
-
Bist nicht der einzige, der das braucht, ein size für Arrays hat wohl jeder C++-Programmierer schon seit jeher in seinem Code.
Nur für generischen Code bringt es halt nichts, denn was will man damit schon anstellen? Fehlt noch ein nonmember-at(), dann hätten wir eine schlechte Kopie von RAIters.
-
Die übliche Schreibweise ist eben mit begin/end, was die gleiche Funktion erfüllt. Oder was würdest du sonst mit der size so machen, was häufig vorkäme?
-
Ehrlich gesagt find ich auch std::begin()/end() unnötig.
Das Argument war, die begin/end von built-in Arrays zu erhalten.
Und im gleichen Standard kam dann std::array...
Edit: OK, man könnte inkompatible Container kompatibel machen, aber ich kenne keine Library mit Containern, die nicht eh schon mit der STL kompatibel ist.
-
std::begin/end wurde eingeführt, im den range-for-loop zu ermöglichen.
-
schwein schrieb:
ein size für Arrays hat wohl jeder C++-Programmierer schon seit jeher in seinem Code
Nein. Erst recht nicht, seit ich keine Arrays mehr benutze.
Ich kann mir gerade keine Situation vorstellen, wo ein globales
size()wirklich Vorteile brächte.
-
Ich sehe schon, das wäre völliger Blödsinn.
Aber wenn wir schon dabei sind, ich habe noch mehr verrückte Ideen: Ich wünsche mir ein Sprachfeature, dass einem Ausdruck erlaubt, in mehrere (durch Kommas getrennte) Ausdrücke zu zerfallen. Beispiel:
class vector { operator list_proxy<iterator, iterator>() { return {begin(), end()}; } };list_proxyselbst zerfällt dann (durch Compiler-Hokuspokus) in eine syntaktische Liste aller Bestandteile.std::copy( vec, iter ); // => std::copy( std::get<0>(list_proxy<iterator, iterator>::tuple()), std::get<1>(list_proxy<iterator, iterator>::tuple()), iter );Alter Code muss nicht verändert werden; Overload-Resolution lässt alle anderen Fälle besser aussehen.
Nicht böse sein, bin angetrunken.
-
camper schrieb:
std::begin/end wurde eingeführt, im den range-for-loop zu ermöglichen.
Aber warum sagen dann manche, man solle bei den Funktionen die globalen statt der Methoden verwenden, also
std::sort(std::begin(container), std::end(container));anstatt
std::sort(container.begin(), container.end());und begründen, dass man damit auch zu built-in Arrays konsistent wäre, man gleichzeitig aber keine Builtin-Arrays sondern std::array verwenden soll.
Sone schrieb:
Ich wünsche mir ein Sprachfeature, dass einem Ausdruck erlaubt, in mehrere (durch Kommas getrennte) Ausdrücke zu zerfallen. Beispiel:
class vector { operator list_proxy<iterator, iterator>() { return {begin(), end()}; } };list_proxyselbst zerfällt dann (durch Compiler-Hokuspokus) in eine syntaktische Liste aller Bestandteile.std::copy( vec, iter ); // => std::copy( std::get<0>(list_proxy<iterator, iterator>::tuple()), std::get<1>(list_proxy<iterator, iterator>::tuple()), iter );Hier würde ich mir eine Art range-class wünschen, also sowas wie
std::sort(container.range());
-
Marthog schrieb:
man gleichzeitig aber keine Builtin-Arrays sondern std::array verwenden soll.
Man soll nicht einfach std::array nehmen.
Normale Arrays haben alle Vorzüge von std::array, dafür aber noch eine nettere Syntax und sie kopieren sich nicht einfach so (weil das teuer ist und besser vermieden wird).
Wer Kopien explizit will, und nur dann, soll std::array verwenden. std::expensive_copyable_array wäre ein besserer Name gewesen.
-
anti-stdarray-fraktion schrieb:
Normale Arrays haben alle Vorzüge von std::array, dafür aber noch eine nettere Syntax und sie kopieren sich nicht einfach so (weil das teuer ist und besser vermieden wird).
Ich behaupte das Gegenteil:
std::array ist schöner, weil man ein standard-container-interface hat, man mit einem einfachen und übersichtlichen Operator kopieren oder Vergleichen kann, man zusätzlich noch praktische Funktionen (fill, swap, reverse-iteratoren, bound checks) hat und gleichzeitig alle Vorteile von Builtin-arrays, außer mehrdimensionalen arrays und automatischem herausfinden der Größe hat, dieses aber sehr einfach nachrüsten könnte.
-
anti-stdarray-fraktion schrieb:
Normale Arrays haben alle Vorzüge von std::array
Ähm, nein. Du hast mit rohen Arrays keine Wertsemantik, keine Möglichkeit zur Übergabe und Rückgabe von Funktionen, kein STL-Interface, keine Debug-Checks und weniger Typsicherheit durch array-to-pointer decay.
Die Syntax ist nur gerade bei der Deklaration netter. Sobald du kopieren musst, kannst du ziemlich viel Boilerplate-Code schreiben.
-
std::size und ähnliches wäre sinnvoll, wenn es range Adaptoren a la boost::range gäbe. Da kann man das gut gebrauchen.
-
Könnte man nicht ein generisches
std::size()übertemplate <typename T> size_t size(const T& container) { return distance(begin(container), end(container)); }implementieren (evtl. auch für die Fanatiker mit generischem Rückgabetyp)? Ist dann je nach Kategorie halt langsam; bei Typen wie
std::listkönnte man als Optimierung Überladungen anbieten. Beistd::forward_listwirds schon schwieriger...Aber wie oft braucht ihr denn
size(), gerade bei Containern ohne Random-Access-Iteratoren? Bei Ranges habe ich das Gefühl, einempty()ist viel wichtiger...
-
@Nexus: Das ist doch Blödsinn. Man macht das per SFINAE, wie auch sonst; alle Typen, die eine Funktion
size()haben, bei denen wird das auch aufgerufen.Für alle Typen, die eine Funktion begin/end aber keine Funktion size() haben, kommt deine Variante ins Spiel. Und sonst geht es um Überladungen.
Aber wie oft braucht ihr denn size(), gerade bei Containern ohne Random-Access-Iteratoren? Bei Ranges habe ich das Gefühl, ein empty() ist viel wichtiger...
Mir ging es darum, einige Operationen die nicht von der Gesamtfunktionalität der Klasse abhängen zu entkoppeln und generisch zu machen. Das gilt sowohl für empty(), size() oder auch Typedefs wie value_type (wobei bei letzterem wohl keine schöne direkte Schreibweise wie ein Funktionsaufruf möglich ist*).
* Am ehesten etwas wie
decltype(elem_t(vec)), wobei aber schon das recht hässlich ist.
-
-
otze schrieb:
Sone schrieb:
@Nexus: Das ist doch Blödsinn. Man macht das per SFINAE
Meinst du nicht ADL?
ADL und SFINAE

-
Python hat auch len() als freie, builtin Funktion.

-
Sone schrieb:
otze schrieb:
Sone schrieb:
@Nexus: Das ist doch Blödsinn. Man macht das per SFINAE
Meinst du nicht ADL?
ADL und SFINAE

Der "member function detection" Trick gehört mit zum kranksten was C++ zu bieten hat. Sowas mag dir gefallen, aber in Production-Code hat das mMn. nix verloren. Also nein, sowas macht "man" nicht so.
Und es spricht mMn. auch nichts gegen Overloads + ADL für die paar Klassen die ein spezielles size brauchen.
Speziell nicht wenn man als Default-Implementierung nicht das u.U. langsamereturn distance(begin(x), end(x))macht, sondern einfachreturn x.size().
-
hustbaer schrieb:
Der "member function detection" Trick gehört mit zum kranksten was C++ zu bieten hat. Sowas mag dir gefallen, aber in Production-Code hat das mMn. nix verloren. Also nein, sowas macht "man" nicht so.
Ich weiß jetzt nicht, was für einen Trick du meinst, aber sowas hier
template<class T> auto size(T&& o) -> decltype(o.size()) { return o.size(); }wird dank SFINAE einfach bei Typen T, die kein .size() anbieten, ignoriert, weil da der Rückgabetyp nicht deduziert werden kann. Man muss, wenn man mehrere Overloads anbieten will, die Auflösung nur etwas steuern, sonst gibt's Mehrdeutigkeiten. Also, wenn wir das hier als Überladung hinzufügen
template<class T> auto size(T&& o) -> decltype(std::distance(o.begin(),o.end())) { return std::distance(o.begin(),o.end()); }dann hätten wir für T=vector<int> eine Mehrdeutigkeit. Kein Template ist besser als das andere. Da hilft aber Tag-Dispatching mit Partial Ordering:
namespace size_impl { using std::begin; using std::end; using std::distance; struct fallback {}; struct preferred : fallback {}; template<class T> auto size(T&& o, preferred) -> decltype(o.size()) { return o.size(); } template<class T> auto size(T&& o, fallback) -> decltype(distance(begin(o),end(o))) { return distance(begin(o),end(o)); } } // namespace size_impl template<class T> auto size(T&& o) -> decltype(size(o,size_impl::preferred())) { return size(o,size_impl::preferred()); }(ungetestet)
Da wünscht man sich eigentlich nur noch, dass man das "-> decltype(...)" nicht mehr schreiben muss.

-
Da wünscht man sich eigentlich nur noch, dass man das "-> decltype(...)" nicht mehr schreiben muss.

Genau dafür hast du doch eigens ein Makro definiert (
AUTO_RETURN).Sollte nicht übrigens der Parameter der beiden Hilfsfunktionen eine
const-Referenz sein?Der "member function detection" Trick gehört mit zum kranksten was C++ zu bieten hat.
Finde ich nicht.
Sowas mag dir gefallen, aber in Production-Code hat das mMn. nix verloren.
Pardon? Production Code?
-
@krümelkacker
Ich wusste nicht dass das mit C++11 einfacher geht. Gut zu wissen, danke!
In C++03 ist es ... naja, seht selbst:
http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Member_Functions_at_Compile-TimeTrotzdem finde ich es etwas fragwürdig. Die C++03 Variante mit "more spezialized" Overloads die über ADL gefunden werden mag mehr Tipparbeit sein, da man für jede speziell zu behandelnde Klasse nen eigenen Overload braucht. Aber genau das ist auch irgendwo die Stärke dieser Variante: man weiss was man bekommt. Und muss nicht mit diversen Tricks rummachen, wenn die generische "one for all" Variante mal nicht das macht was man möchte.