std::size (analog zu begin/end)
-
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.
-
Sone schrieb:
Sowas mag dir gefallen, aber in Production-Code hat das mMn. nix verloren.
Pardon? Production Code?
Ja.
Code der "produktiv" genutzt wird, aka. "production code".
Im Gegensatz zu Code der nicht "produktiv" genutzt wird, wie Code von Proof-of-Concept Implementierungen, Prototypen, irgendwelche kleinen Test-Tools, Zeugs was Leute privat zum Spass programmieren etc.
Wobei mit der Definition auch Ad-Hoc Lösungen, Build-Skripts etc. eingeschlossen wären - die sind aber normalerweise nicht gemeint wenn man "production code" sagt/schreibt.
Bzw. auch einfach: http://www.google.com/search?q="production+code"+programming
-
Marthog schrieb:
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.
Wie von Nathan schon leicht angedeutet:
Nathan schrieb:
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.
Wenn du einen nicht STL kompatible Containern hast, aber jede Menge Funktionalität, die darauf angewiesen ist, dann stellst du einfach Überladung für begin() und end() zur Verfügung. Diese rufen dann von deinem Container die "begin" und "end" Funktion auf. Dann kannst du auch das hübsche range-based-for benutzen:
#include <iostream> using namespace std; struct Container { int a[2]; Container() : a{1,2} {} }; int* begin(Container &cont) { return cont.a; } int* end(Container &cont) { return cont.a+sizeof(cont.a)/sizeof(cont.a[0]); } int main() { Container conti; for(auto aa : conti) cout << aa << " "; }