2014-04-20 14 views
9

Ich versuche, eine Funktion zu schreiben, die auf einem std :: Array mit variabler Größe, zum Beispiel funktionieren: AllerdingsPassing std: array um

std::array a<int,5>={1,2,3,4,5}; 
std::array b<int,3>={6,7,8}; 

myFunc(a); 
myFunc(b); 

void myFunc(std::array &p) 
{ 
cout << "array contains " << p.size() << "elements"; 
} 

, es funktioniert nicht, wenn ich die Größe angeben , aber ich möchte, dass die Funktion die Größe aus dem Array erhält. Ich wollte wirklich nicht die Kopie und die dynamische Zuordnung, die Vektor verwendet, ich möchte den von std :: array() zugewiesenen Platz verwenden und möchte nicht, dass der Compiler eine Kopie der Funktion für jede mögliche Größe des Arrays erstellt.

Ich dachte über das Erstellen einer Vorlage, die wie Array funktioniert, aber einen Zeiger auf vorhandene Daten nehmen, anstatt es selbst zuzuordnen, aber das Rad nicht neu erfinden wollen.

Ist das irgendwie möglich?

+0

Übergeben Sie einen Bereich anstelle eines Containers, wenn Sie den * Container * (aber nur die Elemente, wenn überhaupt) nicht ändern möchten. – dyp

+1

BTW. Die '' ist Teil des * Typs *. Das bedeutet a) Ihre ersten beiden Zeilen enthalten Tippfehler und b) Sie können nicht nur das * template * 'std :: array' als Funktionsparameter verwenden (da es sich nicht um einen Typ, sondern um eine Vorlage für Typen handelt). – dyp

+0

@dyp Was ist der Unterschied zwischen dem Ändern eines 'std :: array' und dem Ändern der Elemente eines' std :: array'? – aschepler

Antwort

7
template<typename T, size_t N> 
void myFunc(std::array<T, N> const &p) { 
    cout << "array contains " << p.size() << "elements"; 
} 

std::array ist keine Klasse und kann nicht wie eine Klasse verwendet werden. Es ist eine Vorlage, und Sie müssen die Vorlage instanziieren, um eine Klasse zu erhalten, die wie eine Klasse verwendet werden kann.

Auch die Größe ist Teil des Typs eines Arrays. Wenn Sie einen Container möchten, dessen Größe nicht Teil des Typs ist, können Sie etwas wie std::vector verwenden. Alternativ können Sie eine Vorlage schreiben, die für jedes Objekt mit der Funktionalität funktioniert, die Sie benötigen:

+0

letzter Stil ist Pythonic;) –

1

Dies liegt daran, dass die Größe Teil des Typs Std :: Array ist.

template< 
    class T, 
    std::size_t N 
> struct array; 

Array mit unterschiedlicher Größe ist unterschiedlicher Typ. Sie sollten eine Template-Funktion verwenden und schreiben:

template< typename T, size_t N> 
void myFunc(std::array<T, N> const &p) { 
    std::cout << "array contains " << p.size() << "elements"; 
} 

http://en.cppreference.com/w/cpp/container/array

1

Ziehe die STL Art und Weise: pass Iteratoren (nach Wert) anstelle von Containern auf Ihre Funktionen. Und templize deine Funktionen.

Ihr Code wird sich viel besser mit den Standardalgorithmen integrieren und wird ebenso mit std :: array oder anderen Standardcontainern funktionieren.

z.B. :

template<class InputIterator> 
void myFunc (InputIterator first, InputIterator last); 
+0

danke, bearbeitet. – quantdev

0

Ich möchte meinen Vorschlag zusätzlich zu denen, die bereits vorgeschlagen wurden, werfen.

template<size_t N> 
void myFunc(std::array<int, N> const &p) { 
    cout << "array contains " << N << "elements"; 
} 

By the way, müssen Sie Ihre Variablendeklarationen sein:

std::array<int,5> a={1,2,3,4,5}; // <int, 5> needs to move to right after array. 
std::array<int,3> b={6,7,8}; 
3

Was Sie wirklich wollen, so etwas wie die vorgeschlagene std :: array_ref (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html), die jede Größe akzeptiert und enthält nur einen Zeiger Bereich (oder ein Zeiger und zählen, je nachdem, wie es implementiert ist), nicht Vorlage Bloat für jeden möglichen Container-Typ, Heap allozierten Container oder Paare von Iteratoren an jedem Aufrufpunkt.

Eine andere Option, die Leute vergessen, ist jedoch std :: initializer_list, die effektiv array_ref ist, eine leichte zusammenhängende homogene Ansicht von Elementen. Obwohl sie für Konstruktoren gedacht sind (wobei der Name "Initialisierer" am sinnvollsten ist), können sie auch von generischen Funktionen konsumiert werden.Beachten Sie, dass der Inhalt nicht kopiert wird, wenn er übergeben wird, nur ein Zeigerbereich, was bedeutet, dass Sie initializer_list nicht als Referenz übergeben müssen (und sollten).

Damit können Sie die Reihe von Zahlen direkt in dem Funktionsaufruf übergeben:

void myFunc(std::initializer_list<int> p) 
{ 
    cout << "array contains " << p.size() << "elements"; 
} 

myFunc({1, 2, 3, 4, 5}); 
myFunc({6, 7, 8}); 

oder über eine Zwischengröße:

std::initializer_list<int> a = {1, 2, 3, 4, 5}; 
std::initializer_list<int> b = {6, 7, 8}; 
auto c = {1, 2, 3, 4, 5}; 
auto d = {6, 7, 8}; 
myFunc(a); 
myFunc(b); 
myFunc(c); 
myFunc(d); 

Oder auch, wie es scheint, von der festen Größe angepasst std :: array unter Verwendung einer Konstruktorüberladung, die ein Anfangs-/Endpaar verwendet.

Obwohl ich diese Konstruktor Überladung nicht auf cppreference.com, noch in N4166 aufgelistet. Diese Überladung ist möglicherweise nicht Standard und wird nur in Dinkumwares STL (von Visual Studio 2015 verwendet) gefunden. Außerdem gibt es keine impliziten Konvertierungen von array/vector, so dass Sie letztendlich trotzdem eine Zwischen-Helfer-Template-Funktion schreiben müssen (oder mehr Code schreiben, als wenn Sie einfach zwei Bereiche übergeben würden), was zu schlecht ist. Idealerweise würde std :: array eine Operator-Methode unterstützen, die zu initializer_list oder array_ref hochgestuft wird, die im Standard übernommen werden.