Dennis S. schrieb:> Ist es wirklich notwendig die Größe des Arrays im Parameter von> "printArray" anzugeben?
Das ist der Sinn von std::array - eine fixe Größe ohne dynamische
Speicherverwaltung. Wenn du eine flexible Größe willst, nimm
std::vector.
Außerdem:
https://stackoverflow.com/a/1452738
Niklas G. schrieb:> Das ist der Sinn von std::array. Wenn du eine flexible Größe willst,> nimm std::vector.
Da muss ich wiedersprechen: der Sinn eines Arrays liegt sicher nicht in
der möglichst langatmige Übergabe an Funktionen! Es geht hier auch nicht
um den Funktionsaufruf sondern um die hartcodierte Größe bei der
Defintion von "printArray".
"Klassich" würde ich einfach die Größe als weiteren Parameter übergeben.
> Außerdem:> https://stackoverflow.com/a/1452738
Das ist in diesem Minimalbeispiel nicht relevant, aber vielleicht
hilfreich für andere die hier mitlesen.
Gruß
Vincent H. schrieb:> Aus dem Grund gibts Templates.> template<size_t I>> void printArray( array<int, I> const& field ) {> for( auto item : field ) {> cout << item << "\t";> }> cout << endl;> }
Verstehe, vielen Dank!
Dennis S. schrieb:> Da muss ich wiedersprechen: der Sinn eines Arrays liegt sicher nicht in> der möglichst langatmige Übergabe an Funktionen!
Wie würdest du es denn die gleiche Funktionalität mit weniger Code
erreichen wollen, außer vielleicht ein kürzeres Alias für "std::array"?
Dennis S. schrieb:> Es geht hier auch nicht> um den Funktionsaufruf sondern um die hartcodierte Größe bei der> Defintion von "printArray".
Ja, der Sinn von std::array ist dass die Größe hartkodiert ist.
Dennis S. schrieb:> "Klassich" würde ich einfach die Größe als weiteren Parameter übergeben.
Das bedeutet also, die Größe soll zur Laufzeit flexibel sein. Da nimmt
man einfach einen Vektor, der ist genau dafür da:
1
#include<iostream>
2
#include<vector>
3
4
5
voidprintArray(std::vector<int>const&field){
6
for(autoitem:field){
7
std::cout<<item<<"\t";
8
}
9
std::cout<<endl;
10
}
11
12
intmain(){
13
std::vector<int>myArray_1{1,2,3};
14
printArray(myArray_1);
15
16
return0;
17
}
templates gehen natürlich auch, aber auch dann muss die Größe zur
Compile-Time feststehen.
Vincent H. schrieb:> Aus dem Grund gibts Templates.
Dann kann man aber auch gleich den ganzen Container-Typ als
template-Parameter nehmen, dann klappts gleich auch für vector, list &
co
Niklas G. schrieb:> Wie würdest du es denn die gleiche Funktionalität mit weniger Code> erreichen wollen, außer vielleicht ein kürzeres Alias für "std::array"?
Das war die Frage. Spiel doch mal in Gedanken durch, was passiert wenn
du mit meinem Ansatz in deinem Programm 90.000 Arrays mit 80.000
verschiedenen Größen hast. Was fällt dir auf?
> Ja, der Sinn von std::array ist dass die Größe hartkodiert ist.
Ja... und? Es geht immernoch um den Funktionssignatur.
> Das bedeutet also, die Größe soll zur Laufzeit flexibel sein. Da nimmt> man einfach einen Vektor, der ist genau dafür da:
Nein.. es geht um Arrays nicht um Vektoren.
> templates gehen natürlich auch, aber auch dann muss die Größe zur> Compile-Time feststehen.
Klar.. Arrays halt.. ;-)
Ich weiß deine Mühe zu schätzen, nur leider sind das keine Antworten auf
die Frage. ;-)
Gruß
Εrnst B. schrieb:> Vincent H. schrieb:>> Aus dem Grund gibts Templates.>>> Dann kann man aber auch gleich den ganzen Container-Typ als> template-Parameter nehmen, dann klappts gleich auch für vector, list &> co>>>
1
>template<typenamecontainer>
2
>voidprintContents(containerconst&field){
3
>for(autoitem:field){
4
>cout<<item<<"\t";
5
>}
6
>cout<<endl;
7
>}
8
>
Wenn es Teil der Anforderung ist sicher, ansonsten verstößt es gegen
KISS und produziert ohne SFINAE/concepts oder ähnlichem unschöne
Compilerfehler.
Dennis S. schrieb:> Ich weiß deine Mühe zu schätzen, nur leider sind das keine Antworten auf> die Frage. ;-)
Dann versteh ich deine Frage nicht. Du willst ein std::array und keinen
std::vector haben, also eine fixe Größe, aber du willst die Größe nicht
im Funktions-Header, sondern erst beim Aufruf angeben, aber als
Parameter, d.h. zur Laufzeit flexibel? Klingt widersprüchlich.
Dennis S. schrieb:> Nein.. es geht um Arrays nicht um Vektoren.
Da beide sehr ähnlich sind... Was genau brauchst du vom array, das der
vector nicht bietet?
Dennis S. schrieb:> Spiel doch mal in Gedanken durch, was passiert wenn> du mit meinem Ansatz in deinem Programm 90.000 Arrays mit 80.000> verschiedenen Größen hast. Was fällt dir auf?
Beim Ansatz mit dem Vektor: Nichts. Bei templates und std::array: 80.000
template-Instanzen, ewige Kompilations-Dauer und eine riesige
Programmdatei.
Niklas G. schrieb:> Dann versteh ich deine Frage nicht. [...]
Das ist offensichtlich.
> Da beide sehr ähnlich sind... Was genau brauchst du vom array, das der> vector nicht bietet?
array.exe: 35.315 Byte
array_template.exe: 40.648 Byte
vector.exe 53.028 Byte
Was brauche im vom Vektor, was das Array nicht bietet?
> Beim Ansatz mit dem Vektor: Nichts. Bei templates und std::array: 80.000> template-Instanzen, ewige Kompilations-Dauer und eine riesige> Programmdatei.
Nein leider falsch.. du musst 80.000 mal die gleiche Funktion mit leicht
anderer Signatur schreiben. Uncool? Ja, richtig...
Dennis S. schrieb:>> Da beide sehr ähnlich sind... Was genau brauchst du vom array, das der>> vector nicht bietet?> array.exe: 35.315 Byte> array_template.exe: 40.648 Byte> vector.exe 53.028 Byte> Was brauche im vom Vektor, was das Array nicht bietet?
Ja ich weiß.. das Beispiel skaliert nicht.
Dennis S. schrieb:>> Da beide sehr ähnlich sind... Was genau brauchst du vom array, das der>> vector nicht bietet?> array.exe: 35.315 Byte> array_template.exe: 40.648 Byte> vector.exe 53.028 Byte
Inwiefern beantworten diese Zahlen, was du eigentlich haben willst?
Dennis S. schrieb:> Was brauche im vom Vektor, was das Array nicht bietet?
Flexible Array-Größe in nur 1 Funktion, ohne 80.000 unterschiedliche
Signaturen bzw. template-Instanzen.
Dennis S. schrieb:> du musst 80.000 mal die gleiche Funktion mit leicht> anderer Signatur schreiben.
Beim Vector? Nein, siehe mein Beispiel. Nur mit std::array? Korrekt. Mit
templates? Nein.
Ich hab's mal ausprobiert: Das Beispiel im Anhang nutzt die
template-Version mit 1000 verschiedenen Größen. Es braucht bei mir 40s
zu kompilieren und produziert eine 156KB Programmdatei. Bei 10000 hab
ich nach 11 Minuten abgebrochen. Das ist meiner Meinung nach wenig
praktikabel. Wie willst du std::array mit 80.000 verschieden Größen
nutzen?
Dennis S. schrieb:> Niklas G. schrieb:>> Das ist der Sinn von std::array. Wenn du eine flexible Größe willst,>> nimm std::vector.> Da muss ich wiedersprechen: der Sinn eines Arrays liegt sicher nicht in> der möglichst langatmige Übergabe an Funktionen! Es geht hier auch nicht> um den Funktionsaufruf sondern um die hartcodierte Größe bei der> Defintion von "printArray".
Wenn du das Wort „Sinn“ gegen „Eigenschaft“ austauschst, verstehst du es
vielleicht besser.
Ein std::Array hat halt eine feste Größe, die im Typen selber mit
drinsteckt. Ob Sinn oder nicht, es ist nunmal so.
Nutz es, wie es ist, oder nimm halt was anderes.
Dennis S. schrieb:> Das war die Frage. Spiel doch mal in Gedanken durch, was passiert wenn> du mit meinem Ansatz in deinem Programm 90.000 Arrays mit 80.000> verschiedenen Größen hast. Was fällt dir auf?
Dann hast du ein ernsthaftes Design-Problem.
Oliver
Dennis S. schrieb:> Erscheint mir dann doch etwas unflexibel...
Was erscheint dir unflexibel? Du hast eine Typ spezifiziert und möchtest
nur einen Teil der Typ-Spezifikation übergeben? Dafür brauchst du
Templates.
Auch in C muss man die Länge des Arrays als Teil des Typs angeben. Da
wäre es dann f(int const(*)[3]).
Dennis S. schrieb:> "Klassich" würde ich einfach die Größe als weiteren Parameter übergeben.
Das kannst du doch machen: f(int const*,size_t) und mit
f(&array[0],array.size) aufrufen.
Niklas G. schrieb:> Inwiefern beantworten diese Zahlen, was du eigentlich haben willst?
Es zeigt dir, dass man nicht die komplexesten Lösungen nutzen muss,
sondern die richtigen.
> Flexible Array-Größe in nur 1 Funktion, ohne 80.000 unterschiedliche> Signaturen bzw. template-Instanzen.
Ich brauche doch gar keine flexible Größe.
Oliver S. schrieb:> Wenn du das Wort „Sinn“ gegen „Eigenschaft“ austauschst, verstehst du es> vielleicht besser.>> Ein std::Array hat halt eine feste Größe, die im Typen selber mit> drinsteckt. Ob Sinn oder nicht, es ist nunmal so.>> Nutz es, wie es ist, oder nimm halt was anderes.
Ich stimme dir 100% zu!
Ich nutze gerne Lösungen die passen. Wenn ich ein Feld fester Größe habe
brauche ich keinen Vektor. Warum diese Missionierungsversuche
losgetreten werden kann ich nicht nachvollziehen.
Zumal ja von der entsprechenden Person schlicht die Frage nicht
verstanden wurde.
Gruß
Mikro 7. schrieb:> Auch in C muss man die Länge des Arrays als Teil des Typs angeben. Da> wäre es dann f(int const(*)[3]).
Nicht direkt so hart codiert. Die Signatur wäre ja hier eher
1
f(intarr[],size_tn);
> Das kannst du doch machen: f(int const*,size_t) und mit> f(&array[0],array.size) aufrufen.
Ich übergebe aber ein std::array, kein int const*. Habe das aber ehrlich
gesagt nicht getestet.
Dennis S. schrieb:> Es zeigt dir, dass man nicht die komplexesten Lösungen nutzen muss,> sondern die richtigen.
Danke für diese weisen Worte. Das verrät aber immer noch nicht, was du
eigentlich willst.
Dennis S. schrieb:> Ich brauche doch gar keine flexible Größe.
Ach. Aber dennoch 80.000 verschiedene? Wie rufst du diese 80.000
verschiedenen Varianten überhaupt auf - du legst 80.000 verschiedene
Instanzen von std::array an?
Dennis S. schrieb:> Wenn ich ein Feld fester Größe habe> brauche ich keinen Vektor. Warum diese Missionierungsversuche> losgetreten werden kann ich nicht nachvollziehen.
Wenn du keine Antworten haben willst, frag nicht im Internet.
Dennis S. schrieb:> Wenn ich ein Feld fester Größe habe> brauche ich keinen Vektor.
Dafür aber 80.000 template-Instanzen. Wenn du kein Problem damit hast,
Stunden aufs kompilieren zu warten, hast du deine Lösung.
Dennis S. schrieb:> Mikro 7. schrieb:>> Auch in C muss man die Länge des Arrays als Teil des Typs angeben. Da>> wäre es dann f(int const(*)[3]).> Nicht direkt so hart codiert. Die Signatur wäre ja hier eherf(int arr[],> size_t n);>> Das kannst du doch machen: f(int const*,size_t) und mit>> f(&array[0],array.size) aufrufen.> Ich übergebe aber ein std::array, kein int const*. Habe das aber ehrlich> gesagt nicht getestet.
Achtung: Hier ist die Größe flexibel! Das brauchst du ja nicht, ist also
der falsche Ansatz!
Dennis S. schrieb:> Nicht direkt so hart codiert. Die Signatur wäre ja hier eher>
1
f(intarr[],size_tn);
Damit übergibst du kein C-Array sondern einen Pointer und eine Länge.
Das Array "decayed". Und das kannst du auch mit std::array machen. Du
hast dann aber, genau wie in C, kein Array mehr.
> Ich übergebe aber ein std::array, kein int const*. Habe das aber ehrlich> gesagt nicht getestet.
Du möchtest ein std::array übergeben, an eine Funktion ohne
entsprechende Signatur? Geht nicht ohne Template Magic. Wie soll der
Compiler das denn auflösen wenn die Funktion einmal mit einem Array mit
3 Elementen und einmal mit einem Array mit 4 Elementen aufgerufen wird?!
Niklas G. schrieb:> Danke für diese weisen Worte. Das verrät aber immer noch nicht, was du> eigentlich willst.
Gerne geschehen. Meine Frage wurde übrigens schon im dritten Beitrag
beantwortet (Templates).
> Dennis S. schrieb:> Ach. Aber dennoch 80.000 verschiedene? Wie rufst du diese 80.000> verschiedenen Varianten überhaupt auf - du legst 80.000 verschiedene> Instanzen von std::array an?
Das war ein Gedankenexperiment weil du die Frage nicht verstanden hast
und ich noch die Hoffnung hatte, dass du an der Lösung interessiert
bist.
> Wenn du keine Antworten haben willst, frag nicht im Internet.
Antwort war erwünscht und habe ich erhalten. Habe mich sogar brav
bedankt, wie sich das gehört... Dass du das Thema verfehlst kann man mir
nur bedingt anlasten oder?
Mikro 7. schrieb:> Du möchtest ein std::array übergeben, an eine Funktion ohne> entsprechende Signatur? Geht nicht ohne Template Magic.
Gut zusammengefasst!
Gruß
Niklas G. schrieb:> Wie willst du std::array mit 80.000 verschieden Größen> nutzen?
Man könnte die Funktion mit zwei Iteratoren statt dem container
aufrufen.
also
1
template<typenameT>
2
voidprintRange(constT&begin,constT&end){...}
3
4
printRange(myA.begin(),myA.end());
der std::array-Iterator ist unabhängig von der array-Länge, und
"printRange" wird nur einmal instantiiert.
Ist aber mehr zu tippen, und erlaubt wieder (Oh Schreck!) auch
std::vector-Iteratoren oder gar C-Style Plain Pointer.
Dennis S. schrieb:> Das war ein Gedankenexperiment weil du die Frage nicht verstanden hast> und ich noch die Hoffnung hatte, dass du an der Lösung interessiert> bist.
Ach, gut zu wissen. Was genau sollte das Experiment zeigen, außer
Nebelkerzen?
Dennis S. schrieb:> Dass du das Thema verfehlst kann man mir> nur bedingt anlasten oder?
Anforderungen zu stellen die nur Experimente ohne Relevanz sind, trägt
nicht zur Verständlichkeit der Anfrage bei.
Εrnst B. schrieb:> Man könnte die Funktion mit zwei Iteratoren statt dem container> aufrufen.> also> template<typename T>> void printRange( const T& begin, const T& end ) {...}>> printRange(myA.begin(),myA.end());
Oder:
1
printRange(std::begin(myA),std::end(myA));
Dann funktionert das auch, wenn myA ein C-Array ist (wohlgmerkt: Ein
Array, nicht ein Zeiger!)
Niklas G. schrieb:> Anforderungen zu stellen die nur Experimente ohne Relevanz sind, trägt> nicht zur Verständlichkeit der Anfrage bei.
Ach lass es doch gut sein. Du bist der einzige der hier die Frage nicht
verstanden hat und trotzdem seinen Senf dazugibt.
Nicht dass ich neue Denkansätze nicht begrüße, aber das sollten bei
einer so konkreten Frage doch eher Alternativen darstellen. Wenn die
erste Antwort aber keinen Bezug zur Frage hat ist das nicht zielführend.
Gruß
Dennis S. schrieb:> Ach lass es doch gut sein. Du bist der einzige der hier die Frage nicht> verstanden hat und trotzdem seinen Senf dazugibt.
Und eine sinnvolle Reaktion auf meine unpassende "vector"-Antwort ist
es, ein irrelevantes Gedankenexperiment zu schreiben, welches die
Alternative mit templates als völlig ungeeignet, meine Antwort hingegen
als passend erscheinen lässt?
Dennis S. schrieb:> Wenn die> erste Antwort aber keinen Bezug zur Frage hat ist das nicht zielführend.
Ah, zu welchem Punkt in deinem Ausgangsposting passt meine Antwort denn
nicht? Woran ist zu erkennen, dass die klassische vector-Lösung nicht
passend ist? Ich finde, eine funktionsfähige(!) Antwort mit einem
einfachen, klassischen Ansatz hat durchaus einen gewissen Bezug zur
Frage.
Wo in deidem Ausgangsposting ist ersichtlich, dass du nicht einfach
arrays falsch verstanden hast und die vector-Variante kennst, aber
explizit nicht haben willst?
Dennis S. schrieb:> "Klassich" würde ich einfach die Größe als weiteren Parameter übergeben.
Wenn du so etwas schreibst - woran ist ersichtlich, dass es zwar in
Ordnung ist, klassisch die Größe Laufzeit-Flexibel als Parameter zu
übergeben, aber nicht in Ordnung ist, die Größe Laufzeit-Flexibel per
vector zu übergeben?
Nur der Vollständigkeit halber.
Man könnte auch gsl::span
(https://github.com/Microsoft/GSL/blob/master/include/gsl/span)
verwenden.
Damit kann man dann jeden Container mit direkt aufeinander folgenden
Elementen in die Funktion stecken (z.B. c-array, std::array,
std::vector).
Außerdem ist damit auch automatisch im Quellcode ersichtlich, dass die
Längenangabe sich auf den pointer bezieht.
M.K. B. schrieb:> Nur der Vollständigkeit halber.>> Man könnte auch gsl::span> (https://github.com/Microsoft/GSL/blob/master/inclu...)> verwenden.> Damit kann man dann jeden Container mit direkt aufeinander folgenden> Elementen in die Funktion stecken (z.B. c-array, std::array,> std::vector).>> Außerdem ist damit auch automatisch im Quellcode ersichtlich, dass die> Längenangabe sich auf den pointer bezieht.
Was will man mit der Lib von Microsoft?
Einfach C++ stdlib (libstdc++ mit parallel Mode ist eine feine Sache)
oder boost benutzen, da gibt es auch nette Container.
Aber das ist hier sowieso vollkommen egal - hier begrenzt in jedem Fall
die Konsole selbst die Geschwindigkeit, sprich man kann sich den Mist
mit dem Array und Templates schenken und direkt auf std::vector setzen.
Ich würde daher dazu raten einfach einen Vektor zu nehmen. Mit -O2 bzw.
-O3 optiminiert der Compiler das so oder so schön sauber, da verliert
man in der Regel keine Performance, selbst wenn wäre diese zu
vernachlässigen (siehe cout). Bootleneck ist eigentlich eh immer der
Durchsatz zwischen RAM und CPU. Selbst L3 Cache ist schon schnarchlahm.
Manche überschätzen ihre Optimierungen oftmals maßlos, denn in der Regel
sind diese vollkommen nutzlos. Der Compiler kann es eh besser und wenn
man keine Pointeraction veranstaltet, dann kann der Compiler oftmals
besser optimieren. Zudem wird der Code übersichtlicher. Manche
optimieren maßlos an Funktionen die für 1% der Laufzeit verantwortlich
sind während man an anderen Stellen teils mit ein paar Änderungen
bereits eine Beschleunigung um 30% erzielen kann.
Gerade mit C++17 gibt es derart viele neue Funktionen die es unsinnig
machen irgendwelche antiken "best guides" von 1910 einzuhalten, es hat
sich einfach viel verändert und der Compiler kann so viel optimieren
dass man selbst Gummicode inzwischen schon performant compiliert
bekommt.
Zudem bieten einem die modernen Container auch einen gewissen Schutz vor
Sicherheitslücken oder sonstigen Problemen, bei der Übergabe deiner
Parameter kann es gerade bei modernen Multithread Architekturen
leichtsam passieren dass man mal out of range landet oder sonstiges. Da
muss man dann immer schön die Ranges kontrollieren, klar, mit Valgrind
geht das schön, aber wieso nicht einfach Arbeit an den Compiler
delegieren?
Egon N. schrieb:> Was will man mit der Lib von Microsoft?
Das ist nicht irgendeine "Lib von Microsoft". Sondern das ist eine
Implementierung der "C++ Core Guidleline Support Library". Diese
Implementierung stammt von Microsoft, es gibt aber ebenso noch einige
weitere Implementierungen.
Ausgedacht hat sich die Lib nicht MS, sondern die Autoren der "C++ Core
Guidlelines", unter anderem Bjarne Stroustrup, dem Erfinder von C++. Die
Chancen stehen gut, dass große Teile der GSL ab C++20 direkt in den
Standard wandern.
Da D. schrieb:> Ausgedacht hat sich die Lib nicht MS, sondern die Autoren der "C++ Core> Guidlelines", unter anderem Bjarne Stroustrup, dem Erfinder von C++. Die> Chancen stehen gut, dass große Teile der GSL ab C++20 direkt in den> Standard wandern.
Das kann sein, aber interessiert mich nicht wirklich, ich habe kein Bock
da auf irgendeine Frickelscheisse, Boost und libstdc++ bekomme ich
fertig aus den Repos, nix git und Tausend verschiedene Versionsstände.
Da haben alle in der Firma dann die gleiche Version der Lib, fertig aus.
Und das letzte mal als ich den Microsoft in einer HPC Anwendung
ausprobiert habe war dessen Code nicht mal halb so schnell wie unter
gcc. Da hat der dicke Xeon mehr als doppelt so lang für die gleiche
Berechnung gebraucht wie der i5 im Laptop. Damit ist der Microsoft
Compiler für mich persönlich gestorben.
Egon N. schrieb:> Das kann sein, aber interessiert mich nicht wirklich, ich habe kein Bock> da auf irgendeine Frickelscheisse, Boost und libstdc++ bekomme ich> fertig aus den Repos, nix git und Tausend verschiedene Versionsstände.> Da haben alle in der Firma dann die gleiche Version der Lib, fertig aus
Kannst Du doch machen ... mit git.
> Und das letzte mal als ich den Microsoft in einer HPC Anwendung> ausprobiert habe war dessen Code nicht mal halb so schnell wie unter> gcc.
Du scheinst den C++-Compiler von MS zu meinen? Nur hat der nichts mit
der GSL zu tun ...
wenn ihr euch ernsthaft Gedanken um Programmspeicher wegen duplizierter
Templateinstanzen für verschiedene Arraylängen macht, kann man das ganze
auch nach außen hin ewas schöner machen und intern die Pointer-Variante
aufrufen. die Hoffnung ist, dass der Compiler den Template call inlined
und direkt die "unsichere" Funktion aufruft.