Frage Kann std :: mit Array-Parametern arbeiten und wenn ja, wie?


Ich habe Probleme damit std::begin() und std::end() (von dem iterator Bibliothek) mit Array-Parametern im C-Stil.

void SetOrigin(const double i_point[3]) {
  Vector v;
  std::copy(
    std::begin(i_point), 
    std::end(i_point), 
    v.begin());
  this->setOrigin(v);
}

Dies führt zu dem folgenden Fehler mit Visual Studio 2010 (und ähnlich für Ende):

error C2784: '_Ty *std::begin(_Ty (&)[_Size])' : could not deduce template argument for '_Ty (&)[_Size]' from 'const double []'
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\xutility(995) : see declaration of 'std::begin'

Ändern des Parameters in nicht-const gibt das gleiche Ergebnis.

Versuchen, den Parameter als anzugeben

...
std::begin<const double, 3>(i_point), 
std::end<const double, 3>(i_point),
...

Gibt:

error C2664: '_Ty *std::begin<const double,3>(_Ty (&)[3])' : cannot convert parameter 1 from 'const double []' to 'const double (&)[3]'

Ist es einfach nicht möglich zu verwenden std::begin auf Array-Parameter, weil sie zu Zeigern zerfallen? Gibt es einen Trick, um dies zu umgehen, oder ist es am besten, die Iterator-Funktionen für Array-Parameter nicht zu verwenden?


9
2017-11-18 22:47


Ursprung


Antworten:


Ja, std::begin und std::end  kann arbeiten mit Parametern, die C-artige Arrays sind.

Der Trick besteht darin, einen Parameter zu übergeben, der ein C-artiges Array ist. Wenn Sie ein 1D-Array als normalen Parameter für eine normale Funktion angeben, wird dessen Typ still von "Array von T" auf "Zeiger auf T" eingestellt. Wenn Sie diese Funktion aufrufen, wird nicht das Array (als Array), sondern ein Zeiger auf das erste Element des Arrays übergeben.

Es ist jedoch möglich, ein Array durch Verweis auf eine Funktionsvorlage zu übergeben:

template <class T, size_t N>
void function(T (&array)[N]) {
   // function body here
}

Im Dies Fall, wo Sie ein tatsächliches Array (wenn auch durch Referenz) übergeben, anstatt einen Zeiger, den Sie verwenden können std::begin und std::end sehr gut. Beispielsweise:

template <class T, size_t N>
T sum(T (&array)[N]) { 
    return std::accumulate(std::begin(array), std::end(array), T());
}

Jetzt ein Array zu übergeben ist trivial, wie zum Beispiel:

int array[] = {1, 2, 3, 4};

auto total = sum(array);

std::begin und std::end selbst sind ähnlich implementiert sum--Das Array wird als Referenz übergeben, sodass sie in etwa wie folgt aussehen können:

template <class T, size_t N>
T *begin(T (&array)[N]) { 
    return array; 
}

template <class T, size_t N>
T *end(T (&array)[N]) {
    return array + N;
}

Beachten Sie, dass diese, obwohl sie dem Standard in jüngerer Zeit hinzugefügt wurden, keine besonders knifflige Verwendung von Templates erfordern. Daher sollte die obige Implementierung mit einem einfachen alten C ++ 98-Compiler funktionieren (und wenn der Speicher auch mit pre -Standard-Compiler wie VC ++ 6).


15
2017-11-18 22:58



Beachten Sie zunächst, dass die Parameterdeklaration const double i_point[3] ist absolut äquivalent zu const double* i_point. Das heißt, die Funktion nimmt jeden Zeiger auf double const unabhängig von der Anzahl der angezeigten Elemente. Infolgedessen kennt es nicht die Größe und std::begin() und std::end() kann die Größe nicht ableiten (naja, std::begin() muss die Größe sowieso nicht ableiten).

Wenn Sie wirklich wollen std::begin() und std::end() Sie müssen ein Array mit drei Elementen oder einen Verweis auf solch ein Biest übergeben. Da Sie Arrays nicht nach Wert übergeben können, ist es am besten, sie als Referenz zu übergeben:

void SetOrigin(double const (&i_point)[3]) {
    // ...
}

Diese Funktion akzeptiert nur Arrays mit genau drei Elementen als Argumente: Sie können keinen Zeiger auf drei übergeben doubles oder ein Teil eines größeren Arrays. Im Gegenzug können Sie jetzt verwenden std::begin() und std::end().


4
2017-11-18 22:52



void SetOrigin(const double i_point[3])

ist genauso wie

void SetOrigin(const double i_point[])

oder

void SetOrigin(const double *i_point)

Damit, std::begin und std::end kann es nicht akzeptieren. In C ++ können Sie ein Array nicht übergeben, sondern als Zeiger oder Verweis. Wenn es ein Zeiger ist, trägt es keine Information des übergebenen Arrays.

Ihre Alternativen sind std::vector oder std::array.


1
2017-11-18 22:49



Hast du std :: array angesehen?

Es funktioniert besser mit anderen STL-Komponenten


0
2017-11-18 22:52



Während Sie Ihre Frage nicht direkt beantworten, wurde sie bereits ausreichend beantwortet M M. und Dietmar Kühl) scheinen Sie einige initialisieren zu wollen std::vectorin dieser Funktion. Das heißt, warum nicht einfach haben:

std::vector v;
std::copy(std::begin(x), std::end(x), std::back_inserter(v));
// or std::copy(x, x + 3, std::back_inserter(v));

Statt eines Funktionsaufrufs an Ihre Funktion, die das versucht?

Alternativ könnten Sie Ihre Funktion wie folgt schreiben:

template<typename RandomIterator>
void SetOrigin(RandomIterator start, RandomIterator end)
{
    std::vector<int> v;
    std::copy(start, end, std::back_inserter(v));
    SetOrigin(v);
}

und dann rufe es mit:

double xyz[3];
SetOrigin(std::begin(xyz), std::end(xyz));

0
2017-11-18 22:58