Ich implementiere einen zweidimensionalen Array-Container (wie boost::multi_array<T,2>
, hauptsächlich für die Praxis). Um die Double-Index-Notation (a[i][j]
) zu verwenden, habe ich eine Proxy-Klasse eingeführt row_view
(und , aber ich bin nicht besorgt über die Konsistenz hier), die einen Zeiger auf den Anfang und das Ende der Zeile hält.Gültigkeit des von operator zurückgegebenen Zeigers->
ich auch über Zeilen in der Lage sein wiederholen möchte und über die Elemente innerhalb einer Zeile getrennt:
matrix<double> m;
// fill m
for (row_view row : m) {
for (double& elem : row) {
// do something with elem
}
}
nun die matrix<T>::iterator
Klasse in eine private row_view rv;
hält (was iterieren gemeint ist, über Zeilen) intern Verfolgen Sie die Zeile, auf die der Iterator zeigt. Natürlich setzt iterator
auch dereferenciation Funktionen:
- für
operator*()
, würde man in der Regel zurückgeben möchten einen Verweis. Stattdessen scheint hier das Richtige zu tun, um einenrow_view
Wert zurückzugeben (d. H. Eine Kopie des privatenrow_view
zurückzugeben). Dies stellt sicher, dass beim Weiterschalten des Iterators dierow_view
immer noch auf die vorherige Zeile zeigt. (In gewisser Weise,row_view
wirkt wie eine Referenz würde). für
operator->()
, bin ich mir nicht so sicher. Ich sehe zwei Möglichkeiten:einen Zeiger auf die privaten
row_view
des Iterators:row_view operator->() const { return &rv; }
einen Zeiger auf eine neue
row_view
(eine Kopie der privaten eins). Aufgrund der Speicherlebensdauer müsste dies auf dem Heap zugewiesen werden. Um clean-up zu gewährleisten, würde ich es wickeln in einemunique_ptr
:std::unique_ptr<row_view> operator->() const { return std::unique_ptr<row_view>(new row_view(rv)); }
Offensichtlich 2 mehr korrekt ist. Wenn der Iterator nachoperator->
aufgerufen wird, wird die row_view
, auf die in 1 gezeigt wird, geändert. der einzige Weg, ich denke, kann jedoch davon, wo diese Rolle würde, ist, wenn die operator->
durch seinen vollständigen Namen und der zurückgegebene Zeiger wurde gebunden hieß:
matrix<double>::iterator it = m.begin();
row_view* row_ptr = it.operator->();
// row_ptr points to view to first row
++it;
// in version 1: row_ptr points to second row (unintended)
// in version 2: row_ptr still points to first row (intended)
Dies ist jedoch nicht, wie Sie in der Regel operator->
verwenden würden, . In solch einem Anwendungsfall würden Sie wahrscheinlich operator*
anrufen und einen Verweis auf die erste Zeile behalten. Normalerweise würde man sofort den Zeiger verwenden, um eine Mitgliedsfunktion von row_view
aufzurufen oder auf ein Mitglied zuzugreifen, z. it->sum()
.
Meine Frage ist jetzt, ist dies: Da die ->
Syntax sofortigen Gebrauch schon sagt, ist die Gültigkeit des von operator->
zurückgegebene Zeiger berücksichtigt auf diese Situation beschränkt sein, oder würde ein sicher Implementierung Konto für den obigen „Missbrauch“ ?
Offensichtlich ist Lösung 2 viel teurer, da sie Heap-Zuweisung erfordert. Dies ist natürlich sehr unerwünscht, da eine Dereferenzierung eine ziemlich häufige Aufgabe ist und es keinen wirklichen Bedarf gibt: die Verwendung von operator*
vermeidet diese Probleme, da sie eine dem Stack zugeordnete Kopie der row_view
zurückgibt.
Ich bin ziemlich sicher, dass Sie eine Referenz für 'operator *' und einen Zeiger für 'operator ->' zurückgeben müssen: https://stackoverflow.com/questions/37191290/iterator-overload-member-selection-vs -indirection-operator – NathanOliver
Gemäß [cppreference] (http://en.cppreference.com/w/cpp/language/operators): "Die Überladung von operator -> muss entweder einen rohen Zeiger zurückgeben oder ein Objekt zurückgeben (durch Referenz oder nach Wert), für den der Operator -> wiederum überladen ist. " – Jonas
Was 'operator *' betrifft, habe ich keine Einschränkungen gefunden. Der Compiler beklagt sich sicher nicht. – Jonas