Frage

Ich habe ein paar Funktionen mit einem Prototyp wie folgt geschrieben:

template <typename input_iterator>
int parse_integer(input_iterator &begin, input_iterator end);

Die Idee ist, dass der Anrufer eine Reihe von Zeichen zur Verfügung stellen würde, und die Funktion die Zeichen als Integer-Wert interpretieren würde und es zurückbringen, an einem vorbei an den zuletzt verwendeten Zeichen zu verlassen beginnen. Zum Beispiel:

std::string sample_text("123 foo bar");
std::string::const_iterator p(sample_text.begin());
std::string::const_iterator end(sample_text.end());
int i = parse_integer(p, end);

Dies würde i bis 123 und p "zeigt" auf den Raum vor foo gesetzt verlassen.

Ich habe da (ohne Erklärung) gesagt worden, dass es schlechte Form ist ein Iterator durch Verweis übergeben. Ist es schlechte Form? Wenn ja, warum?

War es hilfreich?

Lösung

Es gibt nichts wirklich falsch, aber es wird die Verwendung der Vorlage sicherlich begrenzen. Sie werden nicht nur einen Iterator durch etwas anderes oder erzeugt wie v.begin() zurück in der Lage zu setzen, da diese Provisorien sein wird. Sie müssen immer zuerst eine lokale Kopie machen, die eine Art von vorformulierten ist nicht wirklich schön zu haben.

Eine Möglichkeit ist es, um eine Überlastung:

int parse_integer(input_iterator begin, input_iterator end, 
                  input_iterator &newbegin);

template<typename input_iterator>
int parse_integer(input_iterator begin, input_iterator end) {
    return parse_integer(begin, end, begin);
} 

Eine andere Möglichkeit ist es, einen Output-Iterator zu haben, wo die Nummer wird in geschrieben werden:

template<typename input_iterator, typename output_iterator>
input_iterator parse_integer(input_iterator begin, input_iterator end,
                             output_iterator out);

Sie werden den Rückgabewert haben den neuen InputIterator zurückzukehren. Und Sie könnten dann einen Inserter Iterator verwenden die analysierten Zahlen in einen Vektor zu setzen oder einen Zeiger sie setzen sich direkt in eine ganze Zahl oder ein Array davon, wenn Sie bereits die Menge der Zahlen kennen.

int i;
b = parse_integer(b, end, &i);

std::vector<int> numbers;
b = parse_integer(b, end, std::back_inserter(numbers));

Andere Tipps

Allgemein gilt:

Wenn Sie einen nicht-const Verweis übergeben, wird der Anrufer nicht wissen, ob der Iterator geändert wird.

Sie könnten einen const Verweis übergeben, aber in der Regel Iteratoren ist klein genug, dass es keinen Vorteil gegenüber vorbei Wert.

In Ihrem Fall:

Ich glaube nicht, dass es etwas falsch mit dem, was Sie tun, außer dass es nicht zu Standard-esque in Bezug auf Iterator Nutzung.

Wenn sie sagen, „nicht passieren by reference“ vielleicht ist das, weil es mehr normal / idiomatische Iteratoren als Wert Parameter zu übergeben, anstatt sie durch konstante Referenz übergeben:. Was Sie getan haben, für den zweiten Parameter

In diesem Beispiel jedoch müssen Sie zwei Werte zurückgeben: die int-Wert analysiert und die neuen / geänderten Iteratorwert; und da eine Funktion nicht zwei Rückgabecodes haben kann, Codierung einen des Rückgabecodes als eine nicht konstante Referenz IMO normal ist.

Eine Alternative wäre es, Code so etwas wie folgt aus:

//Comment: the return code is a pair of values, i.e. the parsed int and etc ...
pair<int, input_iterator> parse(input_iterator start, input_iterator end)
{
}

Meiner Meinung nach, wenn Sie dies tun wollen das Argument sollte ein Zeiger auf den Iterator sein, Sie zu ändern werden. Ich bin kein großer Fan von nicht konstante Referenz Argumente, weil sie die Tatsache, dass der übergebene Parameter verbergen könnte sich ändern. Ich kenne eine Menge von C ++ Benutzern gibt es die mit meiner Meinung zu diesem Thema nicht einverstanden ist - und das ist in Ordnung

.

Doch in diesem Fall ist es so gemeinsam für Iteratoren als Wert Argumente behandelt werden, dass ich denke, es ist eine besonders schlechte Idee Iteratoren passieren durch nicht konstante Referenz und ändern den übergebenen Iterator. Es geht nur gegen den idiomatischen Weg Iteratoren in der Regel verwendet werden.

Da gibt es eine gute Möglichkeit ist, zu tun, was Sie wollen, dass dieses Problem nicht haben, ich glaube, Sie sollten es verwenden:

template <typename input_iterator>
int parse_integer(input_iterator* begin, input_iterator end);

Nun wird ein Anrufer hätte zu tun:

int i = parse_integer(&p, end);

Und es wird klar sein, dass der Iterator geändert werden kann.

By the way, Ich mag auch litb Vorschlag von den neuen Iterator zurückkehrt und die analysierten Werte in einer Position durch einen Ausgang Iterator angegeben setzen.

In diesem Zusammenhang denke ich, dass ein Iterator durch Bezugnahme vorbei durchaus sinnvoll ist, solange es ist gut dokumentiert.

Es ist erwähnenswert, dass Ihr Ansatz (einen Iterator durch Bezugnahme vorbei, um zu verfolgen, wo Sie sind, wenn ein Strom Zeichenüber) ist genau der Ansatz, der von boost :: tokenizer . Insbesondere sieht die Definition des TokenizerFunction Konzept . Insgesamt habe ich kurbeln finden :: tokenizer ziemlich gut gestaltet sein und gut durchdacht.

Ich denke, der Standard Library Algorithmen Iteratoren nach Wert übergibt ausschließlich (jemand wird nun eine offensichtliche Ausnahme zu diesem Beitrag) - das ist der Ursprung der Idee sein kann. Natürlich nichts sagt, dass Sie Ihr eigener Code wie der Standard Library suchen hat!

Ihre Funktionsdeklaration zweiter Parameter fehlt die Referenz, oder?

Wie auch immer, zurück zu Ihrer Frage: Nein, ich habe überhaupt nichts lesen, die sagt, dass Sie nicht Iteratoren als Verweis übergeben sollte. Das Problem mit Referenzen ist, dass sie ermöglicht es Ihnen, das referenzierte Objekt zu ändern. In diesem Fall, wenn Sie den Iterator ändern sind, schrauben Sie möglicherweise die gesamte Sequenz über diesen Punkt hinaus nach oben dadurch Rendern weiter unmöglich zu verarbeiten.

Nur ein Vorschlag: Geben Sie Ihre Parameter sorgfältig

.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top