Nicht typisierte Vorlagenparameter

92

Ich verstehe, dass der Nicht-Typ-Vorlagenparameter ein konstanter integraler Ausdruck sein sollte. Kann jemand Licht ins Dunkel bringen, warum es so ist?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

Ich verstehe, was ein konstanter integraler Ausdruck ist. Was sind die Gründe dafür, dass nicht konstante Typen wie std::stringim obigen Snippet nicht zugelassen werden?

Mahesh
quelle
17
Ein Vorlagenparameter wird zur Kompilierungszeit aufgelöst.
Etienne de Martel

Antworten:

120

Der Grund, warum Sie dies nicht tun können, ist, dass nicht konstante Ausdrücke während der Kompilierungszeit nicht analysiert und ersetzt werden können. Sie können sich zur Laufzeit ändern, was die Generierung einer neuen Vorlage zur Laufzeit erforderlich machen würde. Dies ist nicht möglich, da Vorlagen ein Konzept zur Kompilierungszeit sind.

Folgendes erlaubt der Standard für nicht typisierte Vorlagenparameter (14.1 [temp.param] p4):

Ein Nicht-Typ-Vorlagenparameter muss einen der folgenden (optional lebenslaufqualifizierten) Typen haben:

  • Integral- oder Aufzählungstyp,
  • Zeiger auf Objekt oder Zeiger auf Funktion,
  • Wertreferenz auf Objekt oder Wertreferenz auf Funktion,
  • Zeiger auf Mitglied,
  • std::nullptr_t.
Xeo
quelle
6
@ALOToverflow: Das fällt unter "Zeiger auf Mitglied". Es ist entweder "Zeiger auf Mitgliedsfunktion" oder "Zeiger auf Mitgliedsdaten".
Xeo
4
Es ist anzumerken, dass für den 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 auf sie können zur Kompilierungszeit erstellt werden.
Theodore Murdock
2
In C ++ 20 ist dies jetzt zulässig, vorausgesetzt, der Typ hat eine stark strukturierte Gleichheit, ist ein Literal, keine veränderlichen / flüchtigen Unterobjekte und der Raumschiffoperator ist öffentlich.
Rakete1111
73

Das ist nicht erlaubt.

Dies ist jedoch zulässig:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

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


Illustration:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Ausgabe:

can assign values locally
can assign values locally
can assign values locally...appended some string
Nawaz
quelle
7
@ Mahesh: Weil die Vorlage im Grunde genommen die Adresse dieses std::stringZeigers oder Referenzobjekts ist. Wenn diese Variable lokal wäre, würden Sie möglicherweise bei jedem Aufruf der Funktion unterschiedliche Adressen erhalten.
Xeo
11
@ Mahesh: Sie wissen nicht, wie der Aufrufstapel zur Kompilierungszeit aussieht. Vor Ihrer Funktion gab es möglicherweise 10 andere oder 3 andere oder wie viele andere auch, sodass sich die Adresse auf dem Stapel, an der die Zeichenfolge erstellt wird, von Aufruf zu Aufruf ändern kann. Wenn Sie ein Objekt mit externer Verknüpfung haben, wird dessen Adresse während der Kompilierung / Verknüpfung festgelegt.
Xeo
2
@Xeo "Die Adresse wird während der Kompilierung / Verknüpfung festgelegt. " Oder nicht, für verschiebbaren oder positionsunabhängigen Code.
Neugieriger
1
Diese Antwort scheint (derzeit) nicht die Frage des OP zu beantworten, in der gefragt wurde, warum dieses Verhalten besteht. Diese Antwort wiederholt lediglich das Beispiel des OP, ohne eine Erklärung abzugeben.
Quuxplusone
1
Ich bin zu spät für die Party, es scheint, dass intelligente Zeiger auch nicht funktionieren
Nicholas Humphrey
28

Sie müssen in der Lage sein, Vorlagenargumente zu entstellen

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

Jetzt müsste ein Impl eine eindeutige Folge von Zeichen für eine std::stringoder für jede andere beliebige benutzerdefinierte Klasse erstellen, die einen bestimmten Wert speichert, dessen Bedeutung der Implementierung nicht bekannt ist. Außerdem kann der Wert beliebiger Klassenobjekte zur Kompilierungszeit nicht berechnet werden.

Es ist geplant, Literalklassentypen als Vorlagenparametertypen für Post-C ++ 0x zuzulassen, die durch konstante Ausdrücke initialisiert werden. Diese können entstellt werden, indem die Datenelemente gemäß ihren Werten rekursiv entstellt werden (für Basisklassen können wir beispielsweise die Tiefenüberquerung von links nach rechts anwenden). Aber es wird definitiv nicht für beliebige Klassen funktionieren.

Johannes Schaub - litb
quelle
10

Ein nicht typisiertes Vorlagenargument, das in einer Vorlagenargumentliste bereitgestellt wird, ist ein Ausdruck, dessen Wert zur Kompilierungszeit bestimmt werden kann. Solche Argumente müssen sein:

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

String-Literale sind auch Objekte mit interner Verknüpfung, sodass Sie sie nicht als Vorlagenargumente verwenden können. Sie können auch keinen globalen Zeiger verwenden. Gleitkomma-Literale sind aufgrund der offensichtlichen Möglichkeit von Rundungsfehlern nicht zulässig.

Sadique
quelle
Sie haben ein Zitat verwendet. Woher stammt die Quelle? Ich würde es gerne positiv bewerten, wenn es eine Quelle für das Zitat gibt.
Gabriel Staples