104 Stimmen

Warum kann ich std::string nicht als nicht typischen Vorlagenparameter verwenden?

Ich verstehe, dass nicht-typische Vorlagenparameter ein konstanter ganzzahliger Ausdruck sein sollten. Kann mir jemand erklären, warum das so ist?

template 
void foo()
{
     // ...
}
Fehler C2993: "std::string": Ungültiger Typ für nicht-typischen Vorlagenparameter 'temp'.

Ich weiß, was ein konstanter ganzzahliger Ausdruck ist. Warum werden nicht-konstante Typen wie std::string wie im obigen Ausschnitt nicht erlaubt?

21 Stimmen

Ein Vorlagenparameter wird zur Kompilierzeit aufgelöst.

129voto

Xeo Punkte 126280

Der Grund, warum dies nicht möglich ist, liegt darin, dass nicht konstante Ausdrücke nicht während der Kompilierungszeit analysiert und substituiert werden können. Sie könnten sich während der Laufzeit ändern, was die Erstellung eines neuen Templates zur Laufzeit erfordern würde, was nicht möglich ist, da Templates ein Konzept der Kompilierungszeit sind.

Das lässt der Standard für nichttypische Template-Parameter zu (14.1 [temp.param] p4):

Ein nichttypischer Template-Parameter muss einen der folgenden (optional cv-qualifizierten) Typen haben:

  • Ganzzahliger oder Aufzählungstyp,
  • Zeiger auf ein Objekt oder Zeiger auf eine Funktion,
  • L-Wert-Referenz auf ein Objekt oder L-Wert-Referenz auf eine Funktion,
  • Zeiger auf ein Element,
  • std::nullptr_t.

6 Stimmen

@ALOToverflow: Das fällt unter "Zeiger auf Element". Es ist entweder "Zeiger auf Elementfunktion" oder "Zeiger auf Elementdaten".

7 Stimmen

Es ist zu beachten, dass im Fall von Zeigern auf Objekte (oder Instanzfelder) die Objekte eine statische Speicherdauer und Verknüpfung haben müssen (extern vor C++11, intern oder extern in C++11), damit Zeiger darauf zur Kompilierzeit erstellt werden können.

4 Stimmen

Im C++20 ist dies nun erlaubt, vorausgesetzt der Typ hat starke strukturierte Gleichheit, ist ein Literal, keine veränderlichen/volatilen Subobjekte und der Raumschiff-Operator ist öffentlich.

80voto

Nawaz Punkte 339767

Dies ist nicht erlaubt.

Jedoch ist dies erlaubt:

template  //Pointer auf Objekt
void f();

template  //Referenz auf Objekt
void g();

Siehe §14.1/6,7,8 im C++ Standard (2003).


Illustration:

template  //Pointer auf Objekt
void f()
{
   cout << *temp << endl;
}

template  //Referenz auf Objekt
void g()
{
     cout << temp << endl;
     temp += "...fügte einige Zeichen hinzu";
}

std::string s; //darf nicht lokal sein, da sie eine externe Verbindung haben muss!

int main() {
        s = "kann lokal Werte zuweisen";
        f<&s>();
        g();
        cout << s << endl;
        return 0;
}

~~Ausgabe:

kann lokal Werte zuweisen 
kann lokal Werte zuweisen
kann lokal Werte zuweisen...fügte einige Zeichen hinzu~~

0 Stimmen

@Nawaz - Was ist der Grund, warum dieselbe Regel nicht für lokale Variablen gilt? Ich kann mir keinen Grund vorstellen, warum es nicht erlaubt ist. Danke.

7 Stimmen

@Mahesh: Weil das, worauf die Vorlage im Wesentlichen Vorlagen basiert, die Adresse dieses std::string-Zeigers oder Referenzobjekts ist. Wenn diese Variable lokal war, würden Sie möglicherweise jedes Mal unterschiedliche Adressen erhalten, wenn die Funktion aufgerufen wurde.

0 Stimmen

@Xeo - Selbst wenn eine andere Adresse übergeben wird, wird zur Kompilierzeit eine erforderliche Funktion generiert, die die Adresse entgegennehmen kann. Was ist also das Problem ? Könnten Sie es bitte anhand eines Beispiels erklären ?

31voto

Sie müssen in der Lage sein, Template-Argumente zu manipulieren

template 
void f() {
 // ...
}

f<"foo">();
f<"foo">(); // gleiche Funktion?

Jetzt müsste eine Implementierung eine eindeutige Zeichenfolge für eine std::string oder auch jede andere beliebige benutzerdefinierte Klasse erstellen, die einen bestimmten Wert speichert, dessen Bedeutung der Implementierung nicht bekannt ist. Und zusätzlich können die Werte von beliebigen Klassenobjekten nicht zur Übersetzungszeit berechnet werden.

Es ist geplant, die Verwendung von Literalklassentypen als Vorlagenparametertypen für Post-C++0x (siehe unten) zu erlauben, die durch konstante Ausdrücke initialisiert werden. Diese könnten durch eine rekursive Verschlüsselung der Datenelemente entsprechend ihrer Werte verschlüsselt werden (für Basisklassen können wir z.B. eine tiefenorientierte, von links nach rechts durchlaufende Traversierung anwenden). Aber es wird definitiv nicht für beliebige Klassen funktionieren.


Ab C++20 ist es nun erlaubt, strukturelle Klassentypen als Vorlagenparameter zu verwenden. Im Wesentlichen müssen strukturelle Klassen einen constexpr-Konstruktor, Destruktor und nur strukturelle Typen von Elementen und Basisklassen (wie Skalare, Arrays davon oder Referenzen) haben. Sie dürfen auch nur öffentliche und nicht mutable Basisklassen und Elemente haben. Diese Bestimmungen ermöglichen es dem Compiler, das Argument sinnvoll zu verschlüsseln, wenn die Vorlage mit einem in den Parametertyp konvertierten konstanten Ausdruck instanziiert wird.

1 Stimmen

Wir haben jetzt etwas, das Ihrem Beschriebenen sehr ähnlich ist. Möchten Sie diese Antwort bearbeiten, da sie bereits in die richtige Richtung geht?

0 Stimmen

@Davis, da ich nicht erfahren genug mit Klassentyp-Vorlagenparametern bin, würde ich es begrüßen, wenn Sie die relevanten Änderungen hinzufügen würden.

0 Stimmen

@Davis Ich habe etwas Text hinzugefügt. Würde es begrüßen, wenn du nach Ungenauigkeiten überprüfen würdest!

10voto

Sadique Punkte 22181

Ein Nicht-Typ-Template-Argument, das innerhalb einer Template-Argumentliste bereitgestellt wird, ist ein Ausdruck, dessen Wert zur Kompilierzeit bestimmt werden kann. Solche Argumente müssen sein:

konstante Ausdrücke, Adressen von Funktionen oder Objekten mit externer Verknüpfung oder Adressen von statische Klassenmitgliedern.

Außerdem sind Zeichenfolgenliterale Objekte mit interner Verknüpfung, daher können Sie sie nicht als Template-Argumente verwenden. Sie können auch keinen globalen Zeiger verwenden. Gleitkommazahlen sind nicht erlaubt, aufgrund der offensichtlichen Möglichkeit von Rundungsfehlern.

2 Stimmen

Du hast ein Zitat benutzt, was ist die Quelle? Ich würde es gerne upvoten, wenn es eine Quelle für das Zitat gibt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X