2016-07-21 27 views
3

Ich habe diesen Code:Ist es legal, ein hinteres Ende des C++ - Array-Zeigers in einen Interator umzuwandeln?

#include <set> 

int main() { 
    int array[] = { 0 }; 
    std::set<int> stdset(&array[1], &array[1]); 
} 

die Adresse des Elements rechts über letztes Arrayelement erhält und wandelt es in Iterator. Grundsätzlich das gleiche wie was std::vector::end() tut.

Es ist legal, dies zu tun:

std::vector<int> vec; 
std::set<int> stdset(vec.end(), vec.end()); 

, weil die "letzte" Iterator eine nicht-inclusive Grenze.

Ist es legal, das gleiche mit RAW-Array wie im ersten Code-Snippet zu tun?

+1

Die Standardcontainerbereichskonstruktoren sind Vorlagen, keine Konvertierung ist beteiligt. – user657267

+0

Beiseite: '& Array [0] + 1' ist legal. So ist das noch einfachere "Array + 1". – Hurkyl

+2

Das ist eine gute Frage. Ich bin * gar nicht * sicher, ob '& array [n]' legal ist (im Gegensatz zu 'array + n' oder' & array [0] + n', die legal sind). Es ist unabhängig von Iteratoren Ich denke: Die Frage ist nur, ob der Ausdruck & Array [n] 'legal ist. -> Und hier ist die Q + A: http://stackoverflow.com/questions/988158/take-the-address-of-a-one-past-the-end-array-element-via-subscrip-legal-by -die –

Antwort

-1

Um ehrlich zu sein, ich kenne die Antwort auf Ihre Frage nach keiner Spezifikation. Das Problem, das ich bei Ihrem Ansatz sehe, ist jedoch, dass Sie Ihre Implementierung an Implementierungsdetails in der Bibliothek binden, die Sie verwenden.

Wenn Sie Ihre Implementierung an die Schnittstellen binden, die von den von Ihnen verwendeten Bibliotheken verfügbar gemacht werden, ist es weniger wahrscheinlich, dass der verwendete Code durchbrochen wird, wenn sich die Implementierung dieser Bibliotheken ändert. Dies ist in diesem speziellen Fall möglicherweise nicht sehr relevant, da sich das Speicherlayout eines Arrays in naher Zukunft nicht ändern wird, aber wenn dies der Fall ist, könnte der Entwickler der Laufzeitbibliothek auch die Implementierung der Iterationsfunktionen entsprechend ändern, wenn dies der Fall ist Wenn Sie die offen gelegten Funktionen verwenden, sollte Ihr Code weiterhin wie erwartet funktionieren. Wenn Ihr Code jedoch von Implementierungsdetails der Bibliotheken abhängt, müssen Sie möglicherweise alle Ihre Anwendungsfälle durchgehen und sie entsprechend ändern.

EDIT:

Es tut mir leid, ich glaube nicht, dass ich mich ausgedrückt sehr klar; Ich spreche nicht über Ihren Code, sondern über Ihren Ansatz. Einer der Vorteile der Kapselung besteht darin, dass Code-Komponenten geschrieben werden können, die ihre jeweiligen Aufgaben hervorragend erfüllen, und dann Anwendungen, indem die von mehreren Code-Komponenten bereitgestellten Funktionen kombiniert werden. Mehrere Abstraktionsebenen ermöglichen es uns, die oberen Ebenen zu gestalten, ohne uns um die kleinen Details in den unteren Ebenen kümmern zu müssen.

Wenn die verschiedenen Komponenten, aus denen die gesamte Anwendung besteht, voneinander isoliert sind, können die Komponenten problemlos aktualisiert werden, ohne die Kompatibilität zu beeinträchtigen, solange die Komponenten ihre minimale Schnittstelle beibehalten und sich ihre Implementierung korrekt verhält. Wenn die verschiedenen Komponenten voneinander abhängig gemacht werden, werden Upgrades viel schwieriger, da Änderungen unter Berücksichtigung der internen Strukturen der beteiligten Komponenten vorgenommen werden müssen. Eine scheinbar harmlose Modifikation in einer Komponente auf niedrigerer Ebene (wie das Einfügen einer neuen Elementvariablen zwischen zwei ältere) kann völlig unvorhergesehene Konsequenzen in einem vollständig nicht verwandten Codeabschnitt haben, der "clever" auf der internen Struktur der Komponente beruht. In der Programmierkunst können Sie die Ressourcen verwenden, die Sie finden, um Ihre Eingaben in Ihre Ausgaben zu verwandeln, aber das bedeutet nicht, dass alle Möglichkeiten die gleichen Auswirkungen haben. Wenn Sie sich Sorgen über die Ausführungszeit machen und der Aufwand für den Aufruf einer Funktion inakzeptabel ist, können Sie zusätzliche Zyklen erzielen, indem Sie einen objektorientierten Ansatz überspringen und Ihr Array nach Index iterieren. Wenn die Ausführungszeit nicht so kritisch ist, dass ein Aufruf einer öffentlichen Methode an einer Schnittstelle möglich ist, erhalten Sie kleinere Upgrade-Albträume.

+0

Ich weiß nicht, auf welche Bibliotheken oder Implementierungsdetails Sie sich beziehen, aber das Speicherlayout eines Arrays ist im Standard angegeben und kann nicht geändert werden. – interjay

+0

Vielen Dank für Ihr Feedback, interjay. Ich habe meine Antwort erweitert, falls Sie es interessant finden. Haben Sie einen guten Tag! –

1

Sie können die Adresse von eins nach dem Ende eines Arrays in C++ nehmen.

Sie können das Ergebnis nicht referenzieren und das Ergebnis verwenden, aber Sie können es mit anderen Zeigern zu demselben Array und zu anderen Eins-nach-dem-Ende desselben Arrays vergleichen.

Was Sie in der OP tun, ist ein definiertes Verhalten und repräsentiert einen leeren Bereich.

1

Es ist keine Konvertierung erforderlich. Zeiger unterstützen dieselben Operationen wie Random-Access-Iteratoren (unäre Operatoren *, ++ und + usw.) und können daher als Iteratoren verwendet werden. Standardbibliotheksfunktionen, die Iteratoren verwenden, werden auf den Typ des Iterators gestempelt, so dass sie einen Zeiger aufnehmen, ohne ihn in irgendwas umzuwandeln.

Dies, zusammen mit der Tatsache, dass ein Zeiger auf eine über das Ende eines Arrays gültig ist (solange Sie es nicht dereferenzieren), bedeutet, dass Ihr Code korrekt ist.

1

Sie konvertieren nichts: Zeiger sind gültige Iteratoren über Vektoren.

Es ist zulässig, einen Zeiger auf das Element direkt hinter dem Ende eines Arrays zu setzen. Es ist jedoch nicht zulässig, einen solchen Zeiger zu dereferenzieren.

ANSI C, § 5.7 Ziffer 5:

Wenn ein Ausdruck, der Integraltyp hat hinzugefügt oder von einem Zeiger subtrahiert, wobei das Ergebnis die Art des Zeigers Operanden hat. Wenn der Zeigeroperand auf ein Element eines Array-Objekts zeigt und das Array groß genug ist, zeigt das Ergebnis auf ein Element, das vom ursprünglichen Element versetzt ist, so dass die Differenz der Indizes der resultierenden und ursprünglichen Arrayelemente dem Integralausdruck entspricht. Mit anderen Worten, wenn der Ausdruck P auf das i-te Element eines Array-Objekts zeigt, zeigen die Ausdrücke (P) + N (äquivalent N + (P)) und (P) -N (wobei N den Wert n hat) jeweils zu den i + n-ten und i-n-ten Elementen des Array-Objekts, sofern sie existieren. Wenn der Ausdruck P auf das letzte Element eines Array-Objekts zeigt, verweist der Ausdruck (P) +1 außerdem auf das letzte Element des Array-Objekts, und wenn der Ausdruck Q auf das letzte Element eines Array-Objekts zeigt, Der Ausdruck (Q) -1 zeigt auf das letzte Element des Array-Objekts. Wenn sowohl der Zeigeroperand als auch das Ergebnis auf Elemente desselben Arrayobjekts oder eines nach dem letzten Element des Arrayobjekts zeigen, soll die Auswertung keinen Überlauf erzeugen; Andernfalls ist das Verhalten nicht definiert.

Ich habe keinen Hinweis auf das in einem C++ Standard Online-Angebot finden, und ich habe nicht eine Kopie des C++ Standard besitzen, also kann ich es noch nicht beweisen, auf dem neuesten Stand in Bezug auf C++, aber es wahrscheinlich ist.

TL; DR: Ihr Code ist legal.