Jeder Standard - Container hat ein begin
und end
Verfahren für Iteratoren für diesen Behälter zurückkehrt. C ++ 11 hat jedoch anscheinend freie Funktionen eingeführt, die aufgerufen werden std::begin
und std::end
die die Funktionen begin
und end
member aufrufen . Also anstatt zu schreiben
auto i = v.begin();
auto e = v.end();
du würdest schreiben
auto i = std::begin(v);
auto e = std::end(v);
In seinem Vortrag Writing Modern C ++ sagt Herb Sutter, dass Sie die freien Funktionen jetzt immer verwenden sollten, wenn Sie den Start- oder Enditerator für einen Container möchten. Er geht jedoch nicht ins Detail, warum Sie möchten. Wenn Sie sich den Code ansehen, sparen Sie alle Zeichen. Was die Standardcontainer angeht, scheinen die freien Funktionen völlig nutzlos zu sein. Herb Sutter gab an, dass es Vorteile für nicht standardmäßige Behälter gibt, aber er ging auch hier nicht ins Detail.
Die Frage ist also, was genau die kostenlosen Funktionsversionen von std::begin
und std::end
über das Aufrufen der entsprechenden Mitgliedsfunktionsversionen hinaus tun und warum Sie sie verwenden möchten.
std::
ganze Zeit wiederholen müsste.Antworten:
Wie nennst du
.begin()
und.end()
auf einem C-Array?Freie Funktionen ermöglichen eine allgemeinere Programmierung, da sie anschließend in einer Datenstruktur hinzugefügt werden können, die Sie nicht ändern können.
quelle
end
für statisch deklarierte Arrays (int foo[5]
) mithilfe von Tricks zur Vorlagenprogrammierung verwenden. Sobald es zu einem Zeiger verfallen ist, haben Sie natürlich kein Glück mehr.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
undend
ein C - Array so lange auf , wie Sie nicht bereits auf einen Zeiger abgeklungen sind , sich - @Huw buchstabiert es heraus. Warum Sie möchten: Stellen Sie sich vor, Sie hätten Code überarbeitet, der ein Array zur Verwendung eines Vektors verwendet (oder umgekehrt, aus welchem Grund auch immer). Wenn Siebegin
undend
und möglicherweise ein cleveres Typedeffing verwendet haben, muss sich der Implementierungscode überhaupt nicht ändern (außer vielleicht einigen Typedefs).Betrachten Sie den Fall, wenn Sie eine Bibliothek haben, die eine Klasse enthält:
Es gibt zwei Methoden:
Um über die Werte zu iterieren, müssen Sie von dieser Klasse erben
begin()
undend()
Methoden für Fälle definieren, in denenAber wenn Sie immer verwenden
du kannst das:
wo
SpecialArrayIterator
ist so etwas wie:Jetzt
i
unde
kann legal für die Iteration und den Zugriff auf Werte von SpecialArray verwendet werdenquelle
template<>
Zeilen nicht enthalten . Sie deklarieren eine neue Funktionsüberladung und spezialisieren keine Vorlage.Durch die Verwendung der Funktionen
begin
undend
free wird eine Indirektionsebene hinzugefügt. Normalerweise wird dies getan, um mehr Flexibilität zu ermöglichen.In diesem Fall kann ich mir einige Verwendungszwecke vorstellen.
Die naheliegendste Verwendung sind C-Arrays (keine C-Zeiger).
Eine andere Möglichkeit besteht darin, einen Standardalgorithmus für einen nicht konformen Container zu verwenden (dh dem Container fehlt eine
.begin()
Methode). Angenommen, Sie können den Container nicht einfach reparieren, besteht die nächstbeste Option darin, diebegin
Funktion zu überladen . Herb schlägt vor, dass Sie diebegin
Funktion immer verwenden , um die Einheitlichkeit und Konsistenz Ihres Codes zu fördern. Anstatt sich merken zu müssen, welche Container die Methode unterstützenbegin
und welche Funktion benötigenbegin
.Abgesehen davon sollte die nächste C ++ - Version die Pseudo-Member-Notation von D kopieren . Wenn
a.foo(b,c,d)
nicht definiert, wird es stattdessen versuchtfoo(a,b,c,d)
. Es ist nur ein wenig syntaktischer Zucker, um uns armen Menschen zu helfen, die lieber ein Thema als eine Verbreihenfolge bevorzugen.quelle
Um Ihre Frage zu beantworten, rufen die freien Funktionen begin () und end () standardmäßig nur die Mitgliedsfunktionen .begin () und .end () des Containers auf. Von
<iterator>
automatisch enthalten , wenn Sie eine der Standard - Container verwenden wie<vector>
,<list>
etc., erhalten Sie:Der zweite Teil Ihrer Frage ist, warum Sie die freien Funktionen bevorzugen, wenn sie ohnehin nur die Mitgliedsfunktionen aufrufen. Das hängt wirklich davon ab, welche Art von Objekt
v
sich in Ihrem Beispielcode befindet. Wenn der Typ von v ein Standardcontainertyp ist, spieltvector<T> v;
es keine Rolle, ob Sie die Funktionen free oder member verwenden, sie tun dasselbe. Wenn Ihr Objektv
allgemeiner ist, wie im folgenden Code:Wenn Sie dann die Elementfunktionen verwenden, wird Ihr Code für T = C-Arrays, C-Zeichenfolgen, Aufzählungen usw. unterbrochen. Durch die Verwendung der Nicht-Elementfunktionen kündigen Sie eine allgemeinere Schnittstelle an, die von Personen problemlos erweitert werden kann. Mit der freien Funktionsschnittstelle:
Der Code funktioniert jetzt mit T = C-Arrays und C-Strings. Schreiben Sie nun eine kleine Menge Adaptercode:
Wir können Ihren Code auch mit iterierbaren Aufzählungen kompatibel machen. Ich denke, Herbs Hauptpunkt ist, dass die Verwendung der freien Funktionen genauso einfach ist wie die Verwendung der Mitgliedsfunktionen, und dass Ihr Code abwärtskompatibel mit C-Sequenztypen und vorwärtskompatibel mit Nicht-STL-Sequenztypen (und Future-STL-Typen!) Ist. mit geringen Kosten für andere Entwickler.
quelle
enum
oder einen anderen grundlegenden Typ als Referenz nehmen; Sie sind billiger zu kopieren als indirekt.Ein Vorteil von
std::begin
undstd::end
ist, dass sie als Erweiterungspunkte für die Implementierung einer Standardschnittstelle für externe Klassen dienen.Wenn Sie eine
CustomContainer
Klasse mit bereichsbasierter for-Schleife oder Vorlagenfunktion verwenden möchten, die.begin()
und erwartet.end()
Methoden verwendet, müssen Sie diese Methoden natürlich implementieren.Wenn die Klasse diese Methoden bereitstellt, ist dies kein Problem. Wenn dies nicht der Fall ist, müssen Sie es ändern *.
Dies ist beispielsweise bei Verwendung einer externen Bibliothek, insbesondere einer kommerziellen und einer Closed-Source-Bibliothek, nicht immer möglich.
In solchen Situationen
std::begin
undstd::end
nützlich, da man eine Iterator-API bereitstellen kann, ohne die Klasse selbst zu ändern, sondern freie Funktionen zu überladen.Beispiel: Angenommen, Sie möchten eine
count_if
Funktion implementieren , die einen Container anstelle eines Paares von Iteratoren verwendet. Ein solcher Code könnte folgendermaßen aussehen:Jetzt müssen Sie für jede Klasse, die Sie mit dieser benutzerdefinierten Klasse verwenden möchten
count_if
, nur zwei kostenlose Funktionen hinzufügen, anstatt diese Klassen zu ändern.Jetzt verfügt C ++ über einen Mechanismus namens Argument Dependent Lookup (ADL), der diesen Ansatz noch flexibler macht.
Kurz gesagt bedeutet ADL, dass ein Compiler, wenn er eine nicht qualifizierte Funktion auflöst (dh eine Funktion ohne Namespace, wie
begin
anstelle vonstd::begin
), auch Funktionen berücksichtigt, die in Namespaces seiner Argumente deklariert sind. Beispielsweise:In diesem Fall spielt es keine Rolle, ob qualifizierte Namen vorhanden sind,
some_lib::begin
undsome_lib::end
- da dies auch der FallCustomContainer
istsome_lib::
, verwendet der Compiler diese Überladungen incount_if
.Das ist auch der Grund für
using std::begin;
undusing std::end;
incount_if
. Dies ermöglicht es uns, unqualifiziert zu verwendenbegin
undend
somit ADL zuzulassen und dem Compiler die Auswahl zu ermöglichen,std::begin
undstd::end
wenn keine anderen Alternativen gefunden werden.Wir können den Cookie essen und den Cookie haben - dh eine Möglichkeit haben, eine benutzerdefinierte Implementierung von
begin
/ bereitzustellen,end
während der Compiler auf Standard- Cookies zurückgreifen kann.Einige Notizen:
Aus dem gleichen Grund gibt es andere ähnliche Funktionen:
std::rbegin
/rend
,std::size
undstd::data
.Wie in anderen Antworten erwähnt,
std::
weisen Versionen Überladungen für nackte Arrays auf. Das ist nützlich, aber es ist einfach ein Sonderfall dessen, was ich oben beschrieben habe.Die Verwendung von
std::begin
und Freunden ist besonders beim Schreiben von Vorlagencode eine gute Idee, da diese Vorlagen dadurch allgemeiner werden. Für Nicht-Vorlagen können Sie gegebenenfalls auch Methoden verwenden.PS Mir ist bewusst, dass dieser Beitrag fast 7 Jahre alt ist. Ich bin darauf gestoßen, weil ich eine Frage beantworten wollte, die als Duplikat markiert war, und festgestellt habe, dass hier keine Antwort ADL erwähnt.
quelle
Während die Nichtmitgliedsfunktionen für die Standardcontainer keinen Vorteil bieten, erzwingt ihre Verwendung einen konsistenteren und flexibleren Stil. Wenn Sie zu einem bestimmten Zeitpunkt eine vorhandene Nicht-Standard-Containerklasse erweitern möchten, möchten Sie lieber Überladungen der freien Funktionen definieren, anstatt die Definition der vorhandenen Klasse zu ändern. Für Nicht-Standard-Container sind sie daher sehr nützlich. Wenn Sie immer die kostenlosen Funktionen verwenden, wird Ihr Code flexibler, da Sie den Standard-Container einfacher durch einen Nicht-Standard-Container ersetzen können und der zugrunde liegende Containertyp für Ihren Code transparenter ist unterstützt eine viel größere Vielfalt von Container-Implementierungen.
Aber das muss natürlich immer richtig gewichtet werden und Überabstraktion ist auch nicht gut. Die Verwendung der kostenlosen Funktionen ist zwar keine große Überabstraktion, bricht jedoch die Kompatibilität mit C ++ 03-Code, was in diesem jungen Alter von C ++ 11 möglicherweise immer noch ein Problem für Sie darstellt.
quelle
boost::begin()
/ verwendenend()
, damit es keine wirkliche Inkompatibilität gibt :)begin/end
). Daher würde ich das auch als Inkompatibilität mit reinem C ++ 03 betrachten. Aber wie gesagt, es ist eine eher kleine (und immer kleinere) Inkompatibilität, da C ++ 11 (zumindestbegin/end
insbesondere) sowieso immer mehr Akzeptanz findet.Letztendlich liegt der Vorteil in Code, der so verallgemeinert ist, dass er containerunabhängig ist. Es kann mit einem
std::vector
, einem Array oder einem Bereich arbeiten, ohne den Code selbst zu ändern.Darüber hinaus können Container, auch nicht im Besitz befindliche Container, so nachgerüstet werden, dass sie auch agnostisch durch Code unter Verwendung von Accessoren verwendet werden können, die nicht auf Mitgliederbereichen basieren.
Sehen Sie hier für weitere Details.
quelle