Array-Zerfall auf Zeiger in Vorlagen

Lesezeit: 3 Minuten

Benutzer-Avatar
Günther Piez

Bitte beachten Sie diesen Code:

#include <iostream>

template<typename T>
void f(T x) {
    std::cout << sizeof(T) << '\n';
}

int main()
{
    int array[27];
    f(array);
    f<decltype(array)>(array);
}

Anmerkung der Redaktion: der ursprünglich verwendete Code typeof(array)das ist jedoch eine GCC-Erweiterung.

Dies wird gedruckt

8 (or 4)
108

Im ersten Fall zerfällt das Array offensichtlich zu einem Zeiger und T wird int*. Im zweiten Fall wird T dazu gezwungen int[27]. Ist die Reihenfolge des Zerfalls/der Substitutionsimplementierung definiert? Gibt es eine elegantere Möglichkeit, den Typ dazu zu zwingen int[27]? Neben der Verwendung von std::vector?

  • Wo haben Sie wo einen C++ Compiler gefunden sizeof(int) == 1? Beim zweiten Anruf bekomme ich 108.

    – Rob Kennedy

    8. Dezember 2009 um 0:12 Uhr

  • Ja, gcc ist natürlich schon auf 4 Byte ints hochgerückt 😉 Ich habe beim Generieren eines Testfalls einen Fehler eingeführt.

    – Günther Piez

    8. Dezember 2009 um 0:29 Uhr

  • Ich bin überrascht, dass der zweite Aufruf kompiliert. In C++ können Sie Arrays nicht als Wert übergeben. [edit: Ah, T has the array type, but sizeof(x) would still output 8 or 4. Never mind. :)]

    – Leichtigkeitsrennen im Orbit

    9. Mai 2011 um 22:00 Uhr


  • Netter Necro-Kommentar 🙂 Stöberst du gerade in meinen alten Fragen? 🙂 Der zweite Fall ist das Übergeben des Arrays als Referenz, das habe ich nicht erkannt, als ich gefragt habe. Siehe akzeptierte Antwort

    – Günther Piez

    9. Mai 2011 um 22:12 Uhr

Benutzer-Avatar
AnT – Stoppt die UkroNazis

Verwenden Sie den Referenztyp für den Parameter

template<typename T> void f(const T& x) 
{
  std::cout << sizeof(T);
}

In diesem Fall wird der Array-Typ nicht verfallen.

In ähnlicher Weise können Sie auch den Verfall in Ihrer Originalversion verhindern f wenn Sie das Template-Agument explizit angeben T als Referenz-auf-Array-Typ

f<int (&)[27]>(array);

Erzwingen des Arguments in Ihrem ursprünglichen Codebeispiel T um den Array-Typ zu haben (d. h. Nicht-Referenz-Array-Typ, indem Sie typeof oder durch explizite Angabe des Typs), verhindert nicht den Verfall des Array-Typs. Während T selbst steht für den Array-Typ (wie Sie beobachtet haben), der Parameter x wird weiterhin als Zeiger deklariert und sizeof x wird immer noch auf die Zeigergröße ausgewertet.

  • Ja, aber ich verwende sizeof(T). Trotzdem danke für die schnelle Antwort 🙂

    – Günther Piez

    8. Dezember 2009 um 0:24 Uhr

Das Verhalten dieses Codes wird von C++14 erklärt [temp.deduct.call]:

Ableiten von Template-Argumenten aus einem Funktionsaufruf

Die Ableitung von Vorlagenargumenten erfolgt durch Vergleichen der einzelnen Funktionsvorlagenparametertypen (nennen Sie es P) mit dem Typ des entsprechenden Arguments des Aufrufs (call it A) wie unten beschrieben

und dann unten:

Wenn P ist kein Referenztyp:

  • Wenn A ein Array-Typ ist, wird anstelle von der von der Array-to-Pointer-Standardkonvertierung (4.2) erzeugte Zeigertyp verwendet A zur Typenableitung;

Für den Anruf f(array);wir haben A = int[27]. A ist ein Array-Typ. Also der abgeleitete Typ T ist int *gemäß diesem letzten Aufzählungspunkt.

Wir können aus dem Qualifier „If P ist kein Referenztyp”, durch die dieses Verhalten vielleicht vermieden werden könnte P ein Bezugstyp. Für den Code:

template<typename T, size_t N>
void f(T (&x)[N])

das Symbol P meint T(&)[N], bei dem es sich um einen Referenztyp handelt; und es stellt sich heraus, dass hier keine Konvertierungen angewendet werden. T wird abgeleitet intmit der Art von x Sein int(&)[N].


Beachten Sie, dass dies nur für Funktionsvorlagen gilt, bei denen der Typ vom Argument abgeleitet wird. Das Verhalten wird durch separate Teile der Spezifikation für explizit bereitgestellte Funktionsvorlagenparameter und Klassenvorlagen abgedeckt.

Sie können auch Vorlagen wie die folgenden verwenden:

template <typename T, std::size_t N>
inline std::size_t number_of_elements(T (&ary)[N]) {
    return N;
}

Dieser kleine Trick führt zu Kompilierungsfehlern, wenn die Funktion auf einem Nicht-Array-Typ verwendet wird.

Benutzer-Avatar
Georg Fritzsche

Abhängig von Ihrem Anwendungsfall können Sie dies mithilfe von Referenzen umgehen:

template<typename T>
void f(const T& x) {
    std::cout << sizeof(T);
}

char a[27];
f(a);

Das druckt 27wie gewünscht.

1019300cookie-checkArray-Zerfall auf Zeiger in Vorlagen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy