Wie würde ich in C ++ 11 vorgehen, um eine Funktion (oder Methode) zu schreiben, die ein std :: -Array bekannten Typs, aber unbekannter Größe verwendet?
// made up example
void mulArray(std::array<int, ?>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6> arr2;
std::array<int, 95> arr3;
mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);
Während meiner Suche habe ich nur Vorschläge zur Verwendung von Vorlagen gefunden, aber diese scheinen chaotisch (Methodendefinitionen im Header) und übertrieben für das, was ich erreichen möchte.
Gibt es eine einfache Möglichkeit, diese Funktion zu verwenden, wie dies bei einfachen Arrays im C-Stil der Fall wäre?
std::vector
.std::vector
Gibt es einen Grund, nicht wie von @TravisPessetto empfohlen zu verwenden?Antworten:
Nein , Sie können wirklich nicht tun , wenn Sie Ihre Funktion eine Funktion machen Vorlage (oder eine andere Art von Behälter verwenden, wie ein
std::vector
, wie es in den Kommentaren zu der Frage vorgeschlagen):Hier ist ein Live-Beispiel .
quelle
template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
Die Größe von
array
ist Teil des Typs , sodass Sie nicht ganz das tun können, was Sie wollen. Es gibt ein paar Alternativen.Bevorzugt wäre es, ein Paar Iteratoren zu nehmen:
Verwenden Sie alternativ
vector
anstelle des Arrays, damit Sie die Größe zur Laufzeit und nicht als Teil des Typs speichern können:quelle
Ich habe es unten versucht und es hat nur bei mir funktioniert.
AUSGABE :
1 2 3 4 5 6 7
2 4 6 8 10 12
1 1 1 1 1 1 1 1 1
3 6 9 12 15 18 21
10 20 30 40 50 60
2 2 2 2 2 2 2 2 2
quelle
template
.auto foo(auto bar) { return bar * 2; }
C ++ derzeit nicht gültig ist, obwohl es in GCC7 mit gesetztem C ++ 17-Flag kompiliert wird. Nach dem Lesen hier sind als auto deklarierte Funktionsparameter Teil des Concepts TS, der eventuell Teil von C ++ 20 sein sollte.BEARBEITEN
C ++ 20 enthält vorläufig
std::span
https://en.cppreference.com/w/cpp/container/span
Ursprüngliche Antwort
Was Sie wollen, ist so etwas wie das
gsl::span
, was in der Guideline Support Library verfügbar ist , die in den C ++ Core Guidelines beschrieben ist:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views
Eine Open-Source-Implementierung der GSL nur für Header finden Sie hier:
https://github.com/Microsoft/GSL
Mit
gsl::span
können Sie dies tun:Das Problem dabei
std::array
ist, dass seine Größe Teil seines Typs ist, sodass Sie eine Vorlage verwenden müssen, um eine Funktion zu implementieren, die einestd::array
beliebige Größe hat.gsl::span
speichert andererseits seine Größe als Laufzeitinformation. Auf diese Weise können Sie eine Nicht-Vorlagenfunktion verwenden, um ein Array beliebiger Größe zu akzeptieren. Es werden auch andere zusammenhängende Container akzeptiert:Ziemlich cool, oder?
quelle
Absolut, es gibt in C ++ 11 eine einfache Möglichkeit, eine Funktion zu schreiben, die ein std :: -Array bekannten Typs, aber unbekannter Größe verwendet.
Wenn wir die Arraygröße nicht an die Funktion übergeben können, können wir stattdessen die Speicheradresse des Startpunkts des Arrays zusammen mit einer zweiten Adresse des Endpunkts des Arrays übergeben. Später können wir innerhalb der Funktion diese 2 Speicheradressen verwenden, um die Größe des Arrays zu berechnen!
Ausgabe an der Konsole: 10, 20, 2, 4, 8
quelle
Dies ist möglich, erfordert jedoch einige Schritte, um es sauber zu machen. Schreiben Sie zunächst a
template class
, das einen Bereich zusammenhängender Werte darstellt. Leiten Sie dann einetemplate
Version, die weiß, wie groß diearray
ist, an dieImpl
Version weiter, die diesen zusammenhängenden Bereich einnimmt.Implementieren Sie abschließend die
contig_range
Version. Beachten Sie, dass diesfor( int& x: range )
funktioniertcontig_range
, da ich implementiert habebegin()
undend()
und Zeiger Iteratoren sind.(nicht getestet, aber das Design sollte funktionieren).
Dann in Ihrer
.cpp
Datei:Dies hat den Nachteil, dass der Code, der den Inhalt des Arrays durchläuft, (zur Kompilierungszeit) nicht weiß, wie groß das Array ist, was zu einer Kostenoptimierung führen kann. Dies hat den Vorteil, dass sich die Implementierung nicht im Header befinden muss.
Seien Sie vorsichtig beim expliziten Erstellen von a
contig_range
, als ob Sie es übergeben würden.set
Es wird davon ausgegangen, dass dieset
Daten zusammenhängend sind, was falsch ist, und überall undefiniertes Verhalten zeigen. Die einzigen zweistd
Container, an denen dies garantiert funktioniert, sindvector
undarray
(und Arrays im C-Stil, wie es passiert!).deque
Obwohl der Direktzugriff nicht zusammenhängend ist (gefährlich, er ist in kleinen Stücken zusammenhängend!),list
ist er nicht einmal in der Nähe und die assoziativen (geordneten und ungeordneten) Container sind gleichermaßen nicht zusammenhängend.Also die drei Konstruktoren, die ich wo implementiert habe
std::array
,std::vector
und C-artige Arrays, die im Grunde die Basen abdecken.Die Implementierung
[]
ist ebenfalls einfach, und dazwischenfor()
und[]
das ist das meiste, wofür Sie sich wünschenarray
, nicht wahr?quelle
template
Funktion ohne Implementierungsdetails. DieImpl
Funktion ist keinetemplate
Funktion, sodass Sie die Implementierung problemlos in der.cpp
Datei Ihrer Wahl ausblenden können . Es ist eine wirklich grobe Art der Typlöschung, bei der ich die Fähigkeit extrahiere, über zusammenhängende Container in eine einfachere Klasse zu iterieren und diese dann durchzuleiten ... (währendmultArrayImpl
atemplate
als Argument genommen wird, ist es keintemplate
Selbst).&*
Dereferenzierung des Iterators (der möglicherweise kein Zeiger ist) macht dann einen Zeiger auf die Adresse. Bei zusammenhängenden Speicherdaten sind der Zeiger aufbegin
und der Zeiger auf one-past-theend
ebenfalls Iteratoren mit wahlfreiem Zugriff, und sie sind für jeden zusammenhängenden Bereich über einen Typ vom gleichen TypT
.