stdvector schrieb:> Perfekt, danke
Das perfekt ist anders gemeint, aber das ist wirklich
das perfekte Konstrukt sich ins Knie zu schiessen.
Ich nutzte C++ wirklich gerne,
aber warum man nicht den default Weg abgesichert hat ist mir ein Rätsel.
Den wirklich jeder Anfänger rennt irgendwann in diese Falle.
Klaus schrieb:> warum man nicht den default Weg abgesichert hat
Weil dann die alten C-Profi-Helden über die Kindergartensprache geheult
hätten, die mit ihren unnötigen Features die Performance ausbremst.
Ach je, in eine Falle rennt man als Anfänger vielleicht (ganz bestimmt)
bei std::map, aber doch nicht beim vector.
at() ist mit bounds checking, operator[] ohne. Das gilt für alle
Container, und auch für das einfache C-Array ( das hat halt kein at() ).
Foolproof ist keine Programmiersprache, man sollte immer wissen, was man
tut.
Oliver
Klaus schrieb:> Das perfekt ist anders gemeint, aber das ist wirklich> das perfekte Konstrukt sich ins Knie zu schiessen.
Vielleicht weil Du in einer expliziten for-loop mit Indizierung
iterierst ...?
Klaus schrieb:> stdvector schrieb:>> Perfekt, danke>> Das perfekt ist anders gemeint, aber das ist wirklich> das perfekte Konstrukt sich ins Knie zu schiessen.>> Ich nutzte C++ wirklich gerne,> aber warum man nicht den default Weg abgesichert hat ist mir ein Rätsel.>> Den wirklich jeder Anfänger rennt irgendwann in diese Falle.
C++ hat viele Probleme, aber das ist keins davon. Der Weg, den man
intuitiv wählt, also operator[], ist der schnelle. Wenn man sich für die
Out-of-Bounds-Bedingung interessiert, wird man als Anfänger ja kaum aus
dem Kopf "catch (std::out_of_range)" hinschreiben, sondern einen Blick
in die Doku werfen. Wo das dann alles steht.
Heiner schrieb:> vector.at wirft eine Exception, wenn ein unzulässiger Index übergeben> wird und der []-Operator tut das nicht.
Auch operator[] darf eine Exception werfen, wenn ein Out-of-Bounds
Zugriff erfolgt.
Wilhelm M. schrieb:> Vielleicht weil Du in einer expliziten for-loop mit Indizierung> iterierst ...?
Nein, einfach weil ich ein Freund defensiver Vorgehensweisen bin.
Ich weiss ja, dass Security bei der Erstellung der STL nicht im
Bewustsein war.
Aber umgekehrt, sprich at() ohne Prüfung, waere mir persönlich lieber
gewesen.
Ich nutze vor Jahren einen C Compiler der Boundary check konnte.
Reihenweise ist Code des Teams in Ausnahmen gerannt.
Heutzutage mit TDD vermutlich weniger wichtig.
Ist aber nur meine persönliche Meinung. Ich kann damit Leben.
mh schrieb:> Heiner schrieb:>> vector.at wirft eine Exception, wenn ein unzulässiger Index übergeben>> wird und der []-Operator tut das nicht.>> Auch operator[] darf eine Exception werfen, wenn ein Out-of-Bounds> Zugriff erfolgt.
Auszug aus cppreference:
---
Returns a reference to the element at specified location pos. No bounds
checking is performed.
Notes
Unlike std::map::operator[], this operator never inserts a new element
into the container. Accessing a nonexistent element through this
operator is undefined behavior.
---
Ich denke, das ist unmissverständlich.
Wilhelm M. schrieb:> Ich denke, das ist unmissverständlich.
Ja, wobei das hier nicht so ganz stimmt:
> No bounds checking is performed.
Es ist der Standardlib nicht verboten, bounds checking zu machen. Man
sollte als Programmierer nur nicht davon ausgehen, dass es gemacht wird.
> Accessing a nonexistent element through this operator is undefined behavior.
"undefined behavior" heißt, dass der C++-Standard keinerlei Vorgaben
dazu macht, was in diesem Fall passiert. Wenn der Entwickler der
Standardbibliothek sich dazu entscheidet, in dem Fall eine
out-of-bounds-Exception zu werfen, verstößt er damit nicht gegen den
Standard.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Ich denke, das ist unmissverständlich.>> Ja, wobei das hier nicht so ganz stimmt:>>> No bounds checking is performed.>> Es ist der Standardlib nicht verboten, bounds checking zu machen. Man> sollte als Programmierer nur nicht davon ausgehen, dass es gemacht wird.
Sehe ich anders: hier ist kein Freiraum für eine Implementierung.
>>> Accessing a nonexistent element through this operator is undefined behavior.>> "undefined behavior" heißt, dass der C++-Standard keinerlei Vorgaben> dazu macht, was in diesem Fall passiert.
Nein, das wäre implementation-defined.
Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,
sondern klar definiert, was passiert, allerdings außerhalb der
Festlegungen des Standard.
Wilhelm M. schrieb:> Nein, das wäre implementation-defined.> Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,
Je nun, diese Diskussion,was "undefined behaviour" ist, ist müßig.
Natürlich darf die lib da eine Exception werfen, oder die Anwendung
terminieren, oder "out of bounds" nach stderr schreiben, oder die
Festplatte formatieren. Man darf sich nur nicht drauf verlassen...
libstdc++ bietet z.B debug-Versionen der Container, (-D_GLIBCXX_DEBUG),
die dann operator[] bounds-cheking haben.
Oliver
Wilhelm M. schrieb:> Sehe ich anders: hier ist kein Freiraum für eine Implementierung.
Genau das Gegenteil ist der Fall: Der Freiraum für die Implementierung
ist unendlich groß. Genau das heißt "undefined behavior".
Aber gut: Wenn der Compiler keinen Freiraum hat, was ist denn deiner
Meinung nach das Verhalten, das laut Standard passieren muss, wenn man
einen out-of-Bounds-Zugriff auf einen vector per operator[] versucht?
>> "undefined behavior" heißt, dass der C++-Standard keinerlei Vorgaben>> dazu macht, was in diesem Fall passiert.>> Nein, das wäre implementation-defined.
Implementation-defined heißt, dass für den Compiler ein Verhalten
definiert und dokumentiert sein muss.
> Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,
Doch, denn undefined behavior heißt, dass JEDES Verhalten korrekt ist,
und das beinhaltet auch das Werfen einer Exception.
> sondern klar definiert, was passiert, allerdings außerhalb der> Festlegungen des Standard.
"undefined" bezieht sich auf den Standard - das Verhalten ist nicht
durch den Standard definiert. Der Compiler darf machen, was er will -
auch ein Verhalten definieren. Er muss aber nicht. Der Compiler wird
nicht dazu gezwungen, irgendetwas unvorhersehbares zu tun.
Wilhelm M. schrieb:> Returns a reference to the element at specified location pos. No bounds> checking is performed.> Ich denke, das ist unmissverständlich.
cppref ist unmissverständlich, aber wo steht das im Standard? Ich kann
dort nur finden, dass at() einen Check durchführt, nichts zu operator[].
Ich habe explizit geschrieben, dass eine Exception geworfen wird, nichts
von einem Check. Da, wie Rolf M. geschrieben hat, das behavior undefined
ist, ist das auch zulässig, falls der Standard den Check verbietet.
Wilhelm M. schrieb:> Nein, das wäre implementation-defined.> Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,> sondern klar definiert, was passiert, allerdings außerhalb der> Festlegungen des Standard.
"Implementation defined" ist im C++ Standard die Festlegung, dass die
Implementation an der Stelle ein definiertes und dokumentiertes
Verhalten haben muss. Dein Compilerhersteller würde also verpflichtet
in die Doku zu schreiben, wass passiert, wenn ein out of bounds Zugriff
passiert und das auch konsequent umzusetzen. "Undefined" schreibt das
eben nicht vor, Compiler/Bibliothek dürfen machen, was sie wollen, inkl.
ihr Verhalten festzulegen und zu dokumentieren.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Sehe ich anders: hier ist kein Freiraum für eine Implementierung.>> Genau das Gegenteil ist der Fall: Der Freiraum für die Implementierung> ist unendlich groß. Genau das heißt "undefined behavior".
Es steht dort: no bounds checking. Daher darf das dort auch nicht
ausgeführt werden. Kein Freiraum für eine Implementierung.
Rolf M. schrieb:> Implementation-defined heißt, dass für den Compiler ein Verhalten> definiert und dokumentiert sein muss.
Genau.
>> Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,>> Doch, denn undefined behavior heißt, dass JEDES Verhalten korrekt ist,> und das beinhaltet auch das Werfen einer Exception.
Ok, überzeugt: wenn dort UB steht, dann darf der Hersteller das durch IB
erstzen, muss aber nicht. Nur wenn dort IB steht, muss er ein IB
beschreiben.
Allerdings könnte der Compiler einen Zugriff auf ein std::Array
außerhalb dessen Größe schlicht wegoptimieren (und eventuell noch viel
mehr, was halt bei UB alles so passieren kann), wenn er das erkennen
kann. Ob er das kann, kann ich allerdings nicht sagen.
Da käme die lib gar nicht mehr dazu, die exception zu werfen.
Oliver
Wilhelm M. schrieb:> Rolf M. schrieb:>> Implementation-defined heißt, dass für den Compiler ein Verhalten>> definiert und dokumentiert sein muss.>> Genau.>>>> Bei einem bounds-checking / throw wäre es kein undefined-bevaiour,>>>> Doch, denn undefined behavior heißt, dass JEDES Verhalten korrekt ist,>> und das beinhaltet auch das Werfen einer Exception.>> Ok, überzeugt: wenn dort UB steht, dann darf der Hersteller das durch IB> erstzen, muss aber nicht.
Noch nicht ganz. Die Bezeichnungen beziehen sich auf den Standard
selbst, nicht auf den Compiler. Undefined behavior heißt, dass der
Standard das Verhalten nicht definiert und keinerlei Vorgaben dazu
macht, was passiert. Implementation-defined behavior heißt, dass der
Standard von der Implementation verlangt, dass sie es definiert (und
dokumentiert).
Wenn etwas UB ist, bleibt es also UB, auch wenn der Compiler das
Verhalten definiert.
Rolf M. schrieb:>> Ok, überzeugt: wenn dort UB steht, dann darf der Hersteller das durch IB>> erstzen, muss aber nicht.>> Noch nicht ganz. Die Bezeichnungen beziehen sich auf den Standard> selbst, nicht auf den Compiler. Undefined behavior heißt, dass der> Standard das Verhalten nicht definiert. Implementation-defined behavior> heißt, dass der Standard von der Implementation verlangt, dass sie es> definiert (und dokumentiert).> Wenn etwas UB ist, bleibt es also UB, auch wenn der Compiler das> Verhalten definiert.
Genau so war es gemeint. Denn wo der Standard von UB spricht, kann es im
Sinne des Standards kein IB geben.
Manche Diskussionen verstehe ich nicht....
mh schrieb:> Auch operator[] darf eine Exception werfen, wenn ein Out-of-Bounds> Zugriff erfolgt.
Könnte, aber macht in diesem Zusammenhang keinen Sinn.
Klaus schrieb:> Ich weiss ja, dass Security bei der Erstellung der STL nicht im> Bewustsein war.
Naja...
Ist es nicht klar, was die vector Entwickler sich dabei gedacht haben?
Dann hier zum mitmeißeln:
Eins der oberen Entwicklungsziele war: Performance
Das lässt sich mit Bereichsprüfungen zu Laufzeit nicht realisieren und
genau in einer solchen zeitkritischen Situation sollen diese Container
weiter verwendbar sein.
Aus dem Grund wird eine schnelle und eine sichere (Zugriffs-) Variante
angeboten.
Fakt:
Wenn gesichert ist, dass die Bereichsgrenzen nicht überschritten werden,
besteht kein Grund für weitere Laufzeitprüfungen.
Klar kann man sich ins Knie schießen, wenn man das nicht weiß/beachtet.
Arduino Fanboy D. schrieb:> Ist es nicht klar, was die vector Entwickler sich dabei gedacht haben?
Dann denk mal ein paar Meter weiter. Was ist mit dem std::vector, der im
Debug-Mode die Grenzen überprüfen soll und im Relese nicht. Was machst
du dann? Schreibst du eine Fallunterscheidung in deine Anwendung ein, um
im Debug-Mode at statt [] zu nutzen?
mh schrieb:> Arduino Fanboy D. schrieb:>> Ist es nicht klar, was die vector Entwickler sich dabei gedacht haben?>> Dann denk mal ein paar Meter weiter. Was ist mit dem std::vector, der im> Debug-Mode die Grenzen überprüfen soll und im Relese nicht. Was machst> du dann? Schreibst du eine Fallunterscheidung in deine Anwendung ein, um> im Debug-Mode at statt [] zu nutzen?
Entweder Debug-Mode der libstdc++ oder die Container parametrierbar
halten, und dann ggf. einen anderen oder einen Wrapper verwenden.
mh schrieb:> Dann denk mal ein paar Meter weiter.
Ich habe dir gesagt, was sich die Entwickler dabei gedacht haben, und
nicht was ich davon halte.
Aber wenn du es unbedingt wissen willst:
Ich stimme den Entwicklern zu!
Performance und Sicherheit schließen sich in dem Fall gegenseitig aus.
Vergleich:
Du kommst mir vor, wie jemand, welcher gleichzeitig durch 2 Türen, aus
einem Auto aussteigen will, und weil er das nicht hin bekommt, über die
überflüssige Tür schimpft. (und in der Folge, im Auto verhungert)
Siehe: Buridans Esel
Oliver S. schrieb:> Allerdings könnte der Compiler einen Zugriff auf ein std::Array> außerhalb dessen Größe schlicht wegoptimieren (und eventuell noch viel> mehr, was halt bei UB alles so passieren kann), wenn er das erkennen> kann. Ob er das kann, kann ich allerdings nicht sagen.> Da käme die lib gar nicht mehr dazu, die exception zu werfen.
Schau dir mal std::get<N>(std::array) an. Dann kompiliert dein zur
Kompilierzeit bekannter aber out-of-bounds-Zugriff erst gar nicht.
https://en.cppreference.com/w/cpp/container/array/get
Ben Gunn schrieb:> Oliver S. schrieb:>> Allerdings könnte der Compiler einen Zugriff auf ein std::Array>> außerhalb dessen Größe schlicht wegoptimieren (und eventuell noch viel>> mehr, was halt bei UB alles so passieren kann), wenn er das erkennen>> kann. Ob er das kann, kann ich allerdings nicht sagen.>> Da käme die lib gar nicht mehr dazu, die exception zu werfen.>> Schau dir mal std::get<N>(std::array) an. Dann kompiliert dein zur> Kompilierzeit bekannter aber out-of-bounds-Zugriff erst gar nicht.>> https://en.cppreference.com/w/cpp/container/array/get
Das ist trivial, aber hier nicht gemeint: siehe Eingangspost.
Wilhelm M. schrieb:> mh schrieb:>> Arduino Fanboy D. schrieb:>>> Ist es nicht klar, was die vector Entwickler sich dabei gedacht haben?>>>> Dann denk mal ein paar Meter weiter. Was ist mit dem std::vector, der im>> Debug-Mode die Grenzen überprüfen soll und im Relese nicht. Was machst>> du dann? Schreibst du eine Fallunterscheidung in deine Anwendung ein, um>> im Debug-Mode at statt [] zu nutzen?>> Entweder Debug-Mode der libstdc++ oder die Container parametrierbar> halten, und dann ggf. einen anderen oder einen Wrapper verwenden.
Damit handelt man sich aber ne ganze Menge mehr Komplexität ein, als ein
einfaches
1
#ifdef __CHECK_BOUNDS
2
assert((0<=index)&&(index<size());
3
#endif
oder
1
#ifdef __CHECK_BOUNDS
2
if((index<0)||(size()<=index){
3
throwWhatever();
4
}
5
#endif
im operator[]. Ich glaube libcxx macht sowas.
Arduino Fanboy D. schrieb:> mh schrieb:>> Dann denk mal ein paar Meter weiter.>> Ich habe dir gesagt, was sich die Entwickler dabei gedacht haben, und> nicht was ich davon halte.
Wenn es das ist was sich die Entwickler dabei gedacht haben, warum wird
der Check dann nicht explizit verboten im Standard und warum ist
operator[] dann noexcept? Du musst das ja beantworten können, du weißt
ja, was die Entwickler denken.
> Aber wenn du es unbedingt wissen willst:> Ich stimme den Entwicklern zu!>> Performance und Sicherheit schließen sich in dem Fall gegenseitig aus.
Das kommt darauf an, wie du Performance und Sicherheit definierst. Bei
mir schließt sich das hier nicht aus.
mh schrieb:> Damit handelt man sich aber ne ganze Menge mehr Komplexität ein, als ein> einfaches
Nein, null overhead. bzw. genausoviel Overhead (wie nötig) und in Deiner
Lösung. Nur ohne Präprozessor und total flexibel.
Wilhelm M. schrieb:> Nein, null overhead. bzw. genausoviel Overhead (wie nötig) und in Deiner> Lösung. Nur ohne Präprozessor und total flexibel.
Ich habe nicht Overhead, sondern Komplexität geschrieben. Das ist ein
Unterschied.
Das ist der zweite Satz, der in libstdc++ Doku unter "Using the Debug
Mode" steht.
1
Note that this flag changes the sizes and behavior of standard class templates such as std::vector, and therefore you can only link code compiled with debug mode and code compiled without debug mode if no instantiation of a container is passed between the two translation units.
mh schrieb:> Wenn es das ist was sich die Entwickler dabei gedacht haben, warum wird> der Check dann nicht explizit verboten im Standard und warum ist> operator[] dann noexcept? Du musst das ja beantworten können, du weißt> ja, was die Entwickler denken.
Das ist ein bisschen Problematisch zu beantworten, da die Argumentation
längst nicht nur auf die std Container zutrifft.
Einerseits hat noexcept einen dokumentierenden Charakter und
andererseits erlaubt es weiter gehende (automatische) Optimierungen.
Es ist also schon der Performance zuträglich, welches ja eins der Design
Ziele ist.
mh schrieb:>> Performance und Sicherheit schließen sich in dem Fall gegenseitig aus.> Das kommt darauf an, wie du Performance und Sicherheit definierst. Bei> mir schließt sich das hier nicht aus.
Interessant!
Da scheinst du recht eigenwillige Definitionen zu haben.
Aber ok... jedem sein Himmelreich.
Arduino Fanboy D. schrieb:> mh schrieb:>> Wenn es das ist was sich die Entwickler dabei gedacht haben, warum wird>> der Check dann nicht explizit verboten im Standard und warum ist>> operator[] dann noexcept? Du musst das ja beantworten können, du weißt>> ja, was die Entwickler denken.> Das ist ein bisschen Problematisch zu beantworten, da die Argumentation> längst nicht nur auf die std Container zutrifft.
Ich formuliere es nochmal etwas genauer.
- Warum verbietet der Standard den Bound-Check für
std::vector::operator[] weder explizit noch implizit?
- Warum verlangt der Standerd kein noexcept für std::vector::operator[]?
Arduino Fanboy D. schrieb:> mh schrieb:>>> Performance und Sicherheit schließen sich in dem Fall gegenseitig aus.>> Das kommt darauf an, wie du Performance und Sicherheit definierst. Bei>> mir schließt sich das hier nicht aus.> Interessant!> Da scheinst du recht eigenwillige Definitionen zu haben.> Aber ok... jedem sein Himmelreich.
Was genau ist hier dein Problem? Ich bekomme die Sicherheit im
Debug-Build wenn ich explizit danach frage und die Performance im
Release, ohne etwas in meiner Anwendung zu ändern. Wieso schließt sich
das aus?
mh schrieb:> Wieso schließt sich das aus?
Weil du nicht aus 2 Türen gleichzeitig das Auto verlassen kannst.
Mit dem "Debuggen" betrittst du einen dritten Weg.
z.B. Machst mit der Flex ein Loch ins Dach.
Die "Lösung" ist für die fertige Anwendung irrelevant.
Betrifft also gar nicht die Entscheidung zwischen sicher und performant.
mh schrieb:> - Warum verbietet der Standard den Bound-Check für> std::vector::operator[] weder explizit noch implizit?>> - Warum verlangt der Standerd kein noexcept für std::vector::operator[]?
Der "Standard" verlangt eine einheitliche (minimal) Schnittstelle.
Dazu gehört die "no-throw" Garantie für std::vector::operator[].
Die konkrete Implementierung darf sich unterscheiden.
Solche Freiheiten finden sich noch an Hunderten weiteren Stellen so,
oder so ähnlich.
Warum der Standard da kein noexcept erwartet, kann ich dir nicht sagen.
Allerdings leitet sich der Einsatz von noexcept für
std::vector::operator[] eigentlich zwangsläufig aus der "no-throw"
Garantie ab.
Vielleicht kommt das ja noch. C++11 ist ja noch nicht so ganz alt.
Oliver S. schrieb:> Arduino Fanboy D. schrieb:>> Dazu gehört die "no-throw" Garantie für std::vector::operator[].> Wer garantiert dir das?
? seltsame Frage...
Der "Standard" fordert es ein.
Der Implementierer hat es umzusetzen.
Dann mal etwas deutlicher: wo im Standard steht die generelle
nothrow-Garantie für den operator[]?
Ich zitiere mal das, was cplusplus.com dazu schreibt:
1
Exception safety
2
If the container size is greater than n, the function never throws exceptions (no-throw guarantee).
3
Otherwise, the behavior is undefined.
Die sind allerdings nicht die normative Referenz, daher hätte ich gerne
dazu eine Link auf eine Originalquelle.
Oliver
Da steht kein noexcept.
Bei 22.2.3 "Sequence containers", clause 15:
> The member function at() provides bounds-checked access to container elements.
at() throws out_of_range if n >= a.size().
Ben Gunn schrieb:> Da steht kein noexcept.
Da steht außer der Signatur überhaupt nichts zum operator[] drin. Das
ist dann noch weniger, als ich vermutet habe. Es steht ansonsten nur
drin, daß at() bounds checking durchführt.
Oliver
Arduino Fanboy D. schrieb:> mh schrieb:>> Wieso schließt sich das aus?> Weil du nicht aus 2 Türen gleichzeitig das Auto verlassen kannst.
Mach mit deinen Türen was du willst. Die Analogie ist zu dämlich, um
darauf sinnvoll zu antworten.
Arduino Fanboy D. schrieb:> Warum der Standard da kein noexcept erwartet, kann ich dir nicht sagen.> Allerdings leitet sich der Einsatz von noexcept für> std::vector::operator[] eigentlich zwangsläufig aus der "no-throw"> Garantie ab.> Vielleicht kommt das ja noch. C++11 ist ja noch nicht so ganz alt.
Wie schon mehrfach gefragt, wo steht im Standard etwas von dieser
Garantie?
Was der Standard von std::vector::operator[] fordert steht in dieser
Tabelle in der vorletzten Zeile.
http://eel.is/c++draft/tab:container.seq.opt
Ich zitiere nochmal aus deinem vorherigen Beitrag:
Arduino Fanboy D. schrieb:> Ich habe dir gesagt, was sich die Entwickler dabei gedacht haben, und> nicht was ich davon halte.
Wir haben also festgestellt, dass du keine Ahnung hast, was sich die
Entwickler dabei gedacht haben. Du hast nichtmal einen Blick in den
Standard (das Ergebnis ihre Gedanken) geworfen.
mh schrieb:> Was der Standard von std::vector::operator[] fordert steht in dieser> Tabelle in der vorletzten Zeile.> http://eel.is/c++draft/tab:container.seq.opt
(a[i] <=> *(a.begin() + i)) ^ (noexcept(*(a.begin() + i)) == true)
=> noexcept(a[i]) == true
Kommt nicht ganz hin:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3279.pdf
"... left as a library vendor quality-of-implementation feature ..."
Schließlich müsste man bei Tests sonst einen Signal-Handler bemühen.
Ex falso quodlibet - das Programm in der Situation sowieso alles machen.
Kaum ist man Mal ein paar Stunden weg...
Ihr bestätigt eigentlich nur meine Meinung, dass die Lösung so sehr
fehleranfällig ist und selbst erfahrene Programmierer am Ende über IB /
UB stolpern.
Und falls das falsch rüber kam:
Natürlich ist es sinnvoll dass es einen performanten schnellen Zugriff
gibt.
Aber den hätte man über at() lösen können und den sicheren definierten
Zugriff über [].
Hätte keinem Weh getan und einige C++ Programme währen robuster.
Al Fine schrieb:> using V = std::vector<int>;> static_assert(noexcept(std::declval<V>()[0]));
Die gcc libc++ implementiert das so, aber das ist eben nur deren
Interpretation des Standards.
Klaus schrieb:> Aber den hätte man über at() lösen können und den sicheren definierten> Zugriff über [].>> Hätte keinem Weh getan und einige C++ Programme währen robuster.
Aber warum sollte man es noch schwieriger und mühsamer machen,
performanten Code zu schreiben? Die bei der Performance gesparte Mühe
kann man doch besser da in die Tipparbeit und die nötigen extra
Fallunterscheidungen bei templatisiertem Zugriff über [] investieren.
Al Fine schrieb:> Aber warum sollte man es noch schwieriger und mühsamer machen,> performanten Code zu schreiben?
Ist es doch gar nicht.
At() und [] unterscheiden sich in 2 Buchstaben...
Man muss bei der Diskussion dran denken, das die Standardisierung zu
anderen Zeiten war. Heute sind Compiler deutlich mächtiger und könnten
einiges optimieren.
Viele Codierstandards fordert Defensive Programmierung. Das ist wichtig.
Was bringt mir ein Programm das performant ist und beim ersten Zugriff
undefiniert aussteigt?
Schlechte Performance sieht man sofort und kam dann optimieren.
Schlechte Robustheit passiert erst im Ernstfall.
Welches ist der kritische Fall...?
Klaus schrieb:> At() und [] unterscheiden sich in 2 Buchstaben...
Na, wenn das für dich der Unterschied ist, kannst du ja at() verwenden.
No problem at all.
Mach ich. Ich hab's ja verstanden und bei mir geht Sicherheit vor
Performance.
Ich hatte nur das meiner Meinung nach schlechte Design "Performances vor
Sicherheit" kritisiert. Iss so, ich lebe damit. Aber es fällt mir bei
Code Reviews immer wieder auf.
Warum meckern viele über strcpy?
Ist doch performanter als strncpy...?
😀
Al Fine schrieb:> Klaus schrieb:>> Warum meckern viele über strcpy?>> Ist doch performanter als strncpy...?>> Ja - und warum sollte man keines von beiden verwenden?
siehst du, du bist in die Falle getappt:
man nimmt natürlich strlcpy.
Aber warum pochst du bei str copies auf Sicherheit und bei [] vector
operator nicht? das ist nicht konsequent.
Daher meine Regel:
Immer defensiv, nur bei Performance Problemen darauf verzichten.
Wünsche gute Nacht.
Klaus schrieb:> Aber warum pochst du bei str copies auf Sicherheit und bei [] vector> operator nicht? das ist nicht konsequent.
Naja, wenn out-of-range behaviour das ist, was ein Argument für strncpy
oder strcpy abgibt, macht vermutlich keine der beiden Funktionen genau
das, was man eigentlich haben wollte.
Al Fine schrieb:> mh schrieb:>> Was der Standard von std::vector::operator[] fordert steht in dieser>> Tabelle in der vorletzten Zeile.>> http://eel.is/c++draft/tab:container.seq.opt>> (a[i] <=> *(a.begin() + i)) ^ (noexcept(*(a.begin() + i)) == true)> => noexcept(a[i]) == true>> Kommt nicht ganz hin:> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3279.pdf>> "... left as a library vendor quality-of-implementation feature ..."> Schließlich müsste man bei Tests sonst einen Signal-Handler bemühen.>> Ex falso quodlibet - das Programm in der Situation sowieso alles machen.
Was genau willst du sagen? Wo im Standard steht die von dir hier
genannte noexcept Bedingung? In dem von dir verlinkten Dokument steht es
nicht drin und wäre auch sonst nicht relevant, da es nicht Teil des
Standards ist.
mh schrieb:> Wo im Standard steht die von dir hier> genannte noexcept Bedingung?
Die ist erschlossen anhand der von dir verlinkten Semantik des "a[i]".
Wenn a[i] die semantische Bedeutung "*(a.begin() + i)" hat, was heisst
das dann, wenn "a.begin()" noexcept ist? Oder meinst du eine mögliche
exception ist keine semantische Abweichung?
Al Fine schrieb:> Aber warum sollte man es noch schwieriger und mühsamer machen,> performanten Code zu schreiben?
Wozu müssen selten ausgeführte Pfade (ergo der meiste Code) auf
Performance optimiert werden? Der Unterschied zur sicheren Variante ist
meist völlig insignifikant, wenn überhaupt messbar (besonders auf
superskalaren Prozessoren). Genau dort die Sicherheit in irgendeiner
Form aufwändiger zu machen schlägt sich ausschließlich in
Sicherheitslücken nieder.
Die Checks brauchst du natürlich nicht, weil dir nie Programmierfehler
unterlaufen mit deinen goldenen Händchen, kennt man.
Jemand schrieb:> Wozu müssen selten ausgeführte Pfade (ergo der meiste Code) auf> Performance optimiert werden?
Volle Zustimmung.
Genau dafür gibt es Performance Tools.
Optimiert wird wenn man weiss wo man am effektivsten optimieren kann.
Jemand schrieb:> Wozu müssen selten ausgeführte Pfade (ergo der meiste Code) auf> Performance optimiert werden?
Wieso selten ausgeführt? Ich mache genau dann einen Bounds-Check, wenn
ich den machen will und auch nur dann. Das hat mit Programmierfehlern
nichts zu tun. Die standartisierten Klassen sind so designed, dass sie
für möglichst viele use-cases brauchbar sind. Irgendwie gehört es dann
dazu, dass sich Raw-Pointer, arrays und vectors in templateisiertem Code
ohne größere Probleme austauschen lassen.
Jemand schrieb:> Die Checks brauchst du natürlich nicht, weil dir nie Programmierfehler> unterlaufen mit deinen goldenen Händchen, kennt man.
Danke. Nächster.
Al Fine schrieb:> Die ist erschlossen anhand der von dir verlinkten Semantik des "a[i]".> Wenn a[i] die semantische Bedeutung "*(a.begin() + i)" hat, was heisst> das dann, wenn "a.begin()" noexcept ist?
Nichts, da weder der Operator+, noch der Operator* des Iterators
noexcept sein muss, und die sind auch der entscheidende Teil für die
out-of-bounds-Prüfung.
Es gibt überhaupt nur einen Fall, in dem das Ergebnis von begin()
out-of-bounds ist, und das ist, wenn der Container leer ist. Da ist das
aber kein Fehler, sondern so gedacht.
Rolf M. schrieb:> Nichts, da weder der Operator+, noch der Operator* des Iterators> noexcept sein muss, und die sind auch der entscheidende Teil für die> out-of-bounds-Prüfung.
Ja, gut... wenn dein + operator exceptions wirft, tut er das halt...
Rolf M. schrieb:> Es gibt überhaupt nur einen Fall, in dem das Ergebnis von begin()> out-of-bounds ist, und das ist, wenn der Container leer ist. Da ist das> aber kein Fehler, sondern so gedacht.
Falsch, begin() liefert einen iterator. Der ist immer gültig, aber nicht
immer de-referenzierbar.
Al Fine schrieb:> Falsch,
Nein, richtig, nur von dir falsch interpretiert.
> begin() liefert einen iterator. Der ist immer gültig, aber nicht> immer de-referenzierbar.
Richtig, und wenn er nicht dereferenzierbar ist, ist er das, weil er
out-of-bounds ist (verweist auf eins nach dem letzten Element).
Rolf M. schrieb:> Al Fine schrieb:>> Falsch,>> Nein, richtig, nur von dir falsch interpretiert.>>> begin() liefert einen iterator. Der ist immer gültig, aber nicht>> immer de-referenzierbar.>> Richtig, und wenn er nicht dereferenzierbar ist, ist er das, weil er> out-of-bounds ist (verweist auf eins nach dem letzten Element).
Ja, aber man kann den iterator beliebig inkrementieren und
dekrementieren. Solange man ihn nicht dereferenziert gibt es da kein
Problem. Im speziellen sollte keine Exception bei einem Ausdruck
Worum es die ganze Zeit geht: Es ist sinnlos, um ein a[i] ein try-catch
zu bauen, [i]es sei denn[/i] man schreibt einen Test-Driver oder sowas.
Über kaputten Code zu philosophieren ist müßig.
Al Fine schrieb:> Ja, aber man kann den iterator beliebig inkrementieren und> dekrementieren. Solange man ihn nicht dereferenziert gibt es da kein> Problem. Im speziellen sollte keine Exception bei einem AusdrucknewIter> = iter + X - Y;> fliegen. Auch dann nicht, wenn iter+X oob ist.
Wo steht diese Anforderung im Standard? Für Pointer gilt das z.B. nicht.
mh schrieb:> Al Fine schrieb:>> Ja, aber man kann den iterator beliebig inkrementieren und>> dekrementieren. Solange man ihn nicht dereferenziert gibt es da kein>> Problem. Im speziellen sollte keine Exception bei einem AusdrucknewIter>> = iter + X - Y;>> fliegen. Auch dann nicht, wenn iter+X oob ist.>> Wo steht diese Anforderung im Standard? Für Pointer gilt das z.B. nicht.
Sollte - nicht muss. Werde glücklich mit deinen Iteratoren, die das
machen.
Al Fine schrieb:> mh schrieb:>> Wo im Standard steht die von dir hier>> genannte noexcept Bedingung?>> Die ist erschlossen anhand der von dir verlinkten Semantik des "a[i]".> Wenn a[i] die semantische Bedeutung "*(a.begin() + i)" hat, was heisst> das dann, wenn "a.begin()" noexcept ist? Oder meinst du eine mögliche> exception ist keine semantische Abweichung?
a.begin() kann sein, was es will. Das entscheidende ist die
Dereferenzierung. Wenn a.begin() + i hinter das Ende des Containers
zeigt, dereferenzierst du mit *(a.begin() + i) einen not dereferencable
iterator, und das ist undefined behaviour.
Damit sind wir dann wieder ganz am Anfang dieses Threads, und können
erneut diskutieren, ob UB auch nicht erwartete exceptions einschliesst.
Oliver
Al Fine schrieb:> Wenn mit deinem Konzept schon ein range-for auseinanderfliegt - wozu der> Worte. Mach du einfach...
Warum fliegt welche range-for auseinander? Ein paar mehr erklärende
Worte von dir wären schon hilfreich.
mh schrieb:> Al Fine schrieb:>> Wenn mit deinem Konzept schon ein range-for auseinanderfliegt - wozu der>> Worte. Mach du einfach...>> Warum fliegt welche range-for auseinander? Ein paar mehr erklärende> Worte von dir wären schon hilfreich.
Das würde mich auch interessieren. Zeigerarithmetik darf man nur
innerhalb eines Arrays oder auf eins nach dessen Ende machen. Darüber
hinaus ist das verboten, und zwar auch schon ohne Dereferenzierung. Da
ein Zeiger auch als Random-Access-Iterator genutzt werden kann, würde
ich vermuten, das für diese die gleichen Anforderungen gelten.
Ich meine, es steht mh ja frei, das so zu machen.
Wie ich zitierte
Al Fine schrieb:> "... left as a library vendor quality-of-implementation feature ..."
Wenn er die "Qualität: unbenutzbar" anstrebt, kann er das ja gerne
machen. Kann std-konform sein....
Al Fine schrieb:> Ich meine, es steht mh ja frei, das so zu machen.>> Wie ich zitierte>> Al Fine schrieb:>> "... left as a library vendor quality-of-implementation feature ...">> Wenn er die "Qualität: unbenutzbar" anstrebt, kann er das ja gerne> machen. Kann std-konform sein....
Kannst du mal etwas genauer erklären was du meinst? Du musst hier nicht
zwingend unter 140 Zeichen bleiben. Was steht mir frei "das so zu
machen"? Warum soll was ich mache, was auch immer du meinst, unbenutzbar
sein?
Al Fine schrieb:> Rolf M. schrieb:>> Zeigerarithmetik darf man nur>> innerhalb eines Arrays oder auf eins nach dessen Ende machen.>> Und genau da knallt es schon.
Wo knallt was?
Al Fine schrieb:> Weißt du... mach doch einfach einen eigenen Thread auf mit der Frage.
Ok, du hast also einfach keinen Bock, dich verständlich auszudrücken.
Warum soll er einen neuen Thread aufmachen, um dich zu fragen, wie du
das meinst? Es bleibt weiterhin ein Rätsel, was das ominöse "das" sein
soll, von dem du denkst, dass wir es tun und warum du glaubst, es müsse
dann knallen.
Steht alles oben. Lesen, denken, verstehen. Wenn ihr schlicht zu dämlich
seid, einen deutschen Text zu lesen, kann ich euch da sicher nicht
helfen.
Sonst geht doch einfach mal den Thread zusammen durch und sucht den
Punkt, wo ihr die Pronomen nicht mehr auflösen könnt. Das wäre dann
schon fast so eine Art Selbsthilfegruppe.
Al Fine schrieb:> Steht alles oben. Lesen, denken, verstehen. Wenn ihr schlicht zu dämlich> seid, einen deutschen Text zu lesen, kann ich euch da sicher nicht> helfen.>> Sonst geht doch einfach mal den Thread zusammen durch und sucht den> Punkt, wo ihr die Pronomen nicht mehr auflösen könnt. Das wäre dann> schon fast so eine Art Selbsthilfegruppe.
Wunderbar: endlich wird der Thread hier so, wie wir es gewohnt sind in
dieser Gruppe ;-)