Template-Klasse unvollständige Spezialisierung

8

Ich stieß auf einen interessanten Punkt, den ich nicht erklären konnte oder für den ich keine Erklärung finden konnte. Berücksichtigen Sie die folgende Vorlagendefinition (kompiliert mit mingw g ++ 4.6.2): ​​

%Vor%

Wenn wir wollen, können wir jede einzelne Mitgliedsfunktion vollständig spezialisieren:

%Vor%

Aber teilweise Spezialisierung scheitert mit einer "ungültigen Verwendung von unvollständigen Typ 'Klasse Foo & lt; ... & gt;'" Fehler:

%Vor%

Und ich kann nicht herausfinden warum. Ist es eine bewusste Designentscheidung, um ein Problem zu vermeiden, das ich nicht vorhersehen kann? Ist es ein Versehen?

Vielen Dank im Voraus.

    
StoryTeller 17.12.2012, 22:59
quelle

3 Antworten

6

Der Begriff teilweise Spezialisierung existiert nur für Klassenvorlagen (beschrieben in §14.5.5) und Mitgliedervorlagen (dh Mitglieder einer Vorlagenklasse, die selbst Vorlagenfunktionen sind, beschrieben in §14.5.5.3 / 2). Es existiert weder für normale Member von Klassenvorlagen, noch existiert es für Funktionsvorlagen - einfach weil es nicht vom Standard beschrieben wird.

Nun könnten Sie das argumentieren, indem Sie die Definition einer partiellen Spezialisierung einer Elementfunktion angeben, z. B.

%Vor%

Sie implizit definieren eine partielle Spezialisierung der Klassenvorlage: Foo<T,int> . Das wird jedoch ausdrücklich durch den Standard ausgeschlossen:

  

(§14.5.5 / 2) Jede partielle Spezialisierung der Klassenvorlage ist eine eigene Vorlage, und für die Mitglieder einer partiellen Spezialisierung der Vorlage (14.5.5.3) sind Definitionen anzugeben.

     

(§14.5.5.3 / 1) [...] Die Mitglieder der partiellen Spezialisierung der Klassenschablone haben keinen Bezug zu den Mitgliedern der primären Vorlage. Teilweise Spezialisierungselemente der Klassenvorlage, die in einer Weise verwendet werden, die eine Definition erfordert, müssen definiert werden; Die Definitionen der Mitglieder der primären Vorlage werden niemals als Definitionen für Mitglieder einer partiellen Spezialisierung der Klassenvorlage verwendet. [...]

Letzteres impliziert, dass es unmöglich ist, eine partielle Spezialisierung implizit zu definieren, indem man einfach die Definition eines ihrer Mitglieder angibt: Die bloße Existenz dieses Mitglieds würde nicht aus der Definition der primären Vorlage folgen Daher ist das Definieren von das Definieren einer Elementfunktion, die nicht deklariert ist, und das ist nicht zulässig (selbst bei Nicht-Template-Klassen).

Andererseits gibt es den Begriff explizite Spezialisierung (oder vollständige Spezialisierung , wie Sie ihn nennen) für Memberfunktionen von Klassenvorlagen. Es wird explizit im Standard beschrieben:

  

(§14.7.3 / 1) Eine explizite Spezialisierung von einem der folgenden Punkte:
  [...]
  - Memberfunktion einer Klassenvorlage
  [...]
  kann durch eine Deklaration deklariert werden, die durch die Vorlage & lt; & gt ;; [...]

§14.7.3 / 14 beschreibt die Details:

  

(§14.7.3 / 14) Eine Member- oder Member-Vorlage einer Klassenvorlage kann explizit auf eine gegebene implizite Instanziierung der Klassenvorlage spezialisiert sein, selbst wenn die Member- oder Member-Vorlage in der Klassen-Template-Definition definiert ist. [...]

Bei expliziter Spezialisierung von Mitgliedern funktioniert die Instanziierung des Rests der Klassenvorlage daher implizit - sie wird von der primären Vorlagendefinition oder von allen partiellen Spezialisierungen abgeleitet, falls sie definiert sind.

    
jogojapan 18.12.2012, 02:02
quelle
5

Ich habe versucht, ein knappes Zitat aus dem Standard zu finden, aber ich glaube nicht, dass es einen gibt. Tatsache ist, dass es keine partielle Spezialisierung einer Template-Funktion (oder, in der Tat, eines Template-Alias) gibt. Nur Klassenvorlagen können teilweise spezialisiert sein.

Vergessen wir die Vorlagen für eine Sekunde. In C ++ gibt es einen großen Unterschied zwischen Klassennamen und Funktionsnamen. Es kann nur eine Definition einer Klasse innerhalb eines bestimmten Bereichs geben. (Sie können verschiedene Deklarationen haben, aber sie beziehen sich alle auf die eine wahre Klasse.) Der Name identifiziert also die Klasse wirklich.

Ein Funktionsname hingegen ist eine Art Gruppenidentität. Sie können beliebig viele Funktionen innerhalb eines Bereichs mit exakt demselben Namen definieren. Wenn Sie einen Funktionsnamen verwenden, um eine Funktion aufzurufen, muss der Compiler herausfinden, welche Funktion Sie wirklich verstanden haben, indem Sie die verschiedenen Möglichkeiten betrachten und die Signatur jedes einzelnen mit den angegebenen Argumenten abgleichen. Es gibt keine Beziehung zwischen den verschiedenen Funktionen, die einen Namen teilen; Sie sind völlig separate Entitäten.

Also, keine große Sache. Du wusstest das alles, oder? Aber jetzt gehen wir zurück zu Vorlagen.

Der Name einer Vorlagenklasse ist immer noch einzigartig. Obwohl Sie teilweise Spezialisierungen definieren können, müssen Sie die gleiche Template-Klasse explizit spezialisieren. Dieser Mechanismus sieht oberflächlich wie der oben erwähnte Algorithmus zur Auflösung von Funktionsnamen aus. Es gibt jedoch signifikante Unterschiede. Einer davon ist, dass Sie im Gegensatz zu Funktionsprototypen nicht zwei Klassenvorlagen im selben Bereich mit verschiedenen Arten von Vorlagenparametern haben können / p>

Vorlagenfunktionen müssen dagegen keine eindeutigen Namen definieren. Templating ersetzt nicht den normalen Funktionsüberlastungsmechanismus. Wenn also der Compiler versucht herauszufinden, was ein Funktionsname bedeutet, muss er alle deklarierten Deklarationen für diesen Funktionsnamen berücksichtigen, die Vorlagen in eine Menge von Vorlagenparametrierungen auflösen (wenn möglich) und dann einmal hat eine Liste von möglichen Funktionsobjekten, wählen Sie das beste mit normaler Überladungsauflösung.

Das ist ein ziemlich anderer Algorithmus als die Template-Parameterauflösung der Template-Klasse. Anstatt nur eine Liste der bereitgestellten Template-Argumente mit einer Liste der deklarierten Template-Parameter zu vergleichen, wie sie Klassen-Templates auflöst, muss sie jede Template-Funktion übernehmen, die möglicherweise übereinstimmt (hat zum Beispiel mindestens die richtige Anzahl von Parametern) ; abzuleiten Template-Parameter durch Vereinheitlichung der gelieferten Argumente mit der Vorlage; und fügen Sie dann für eine weitere Runde der Überladungsauflösung die Auflösungsspezialisierung zur Überladungsgruppe hinzu.

Ich vermute, es wäre möglich gewesen, auch in diesen Prozess eine teilweise Spezialisierung zu integrieren, aber die Interaktionen zwischen partieller Spezialisierung und Funktionsüberlastung erscheinen mir wahrscheinlich als pseudo-magisches Verhalten. In diesem Fall war es nicht notwendig und daher gibt es keinen solchen Mechanismus. (Sie können eine Funktionsvorlage vollständig spezialisieren. Vollständige Spezialisierung bedeutet, dass keine Argumente für die Vorlage abgeleitet werden müssen. Es ist also kein Problem.)

Also das ist der Scoop: Sie können eine Vorlagenfunktion nicht teilweise spezialisieren, aber es gibt nichts, was Sie davon abhält, eine beliebige Anzahl von Funktionsschablonen mit dem gleichen Namen bereitzustellen. Alle werden in Überladungsauflösung betrachtet, und der Beste wird wie immer gewinnen.

Normalerweise reicht das für Ihre Überlastungsbedürfnisse aus. Sie sollten über Vorlagenfunktionen genau so nachdenken, wie Sie über normale Funktionen denken: Sie haben die Möglichkeit, basierend auf den angegebenen Argumenten die gewünschte Vorlage auszuwählen. Wenn Sie der Meinung sind, dass Sie Template-Parameter in einem Funktionsaufruf angeben müssen, anstatt sie abzuleiten, machen Sie einfach die Funktion zu einem (möglicherweise statischen) Mitglied einer Template-Klasse und geben Sie die Template-Argumente an die Klasse weiter.

Ich hoffe, das hilft ...

    
rici 18.12.2012 01:13
quelle
3

Ich denke, der Unterschied besteht darin, dass bei der ersten (gültigen) expliziten Spezialisierung von f :

%Vor%

Sie machen eine implizite Instanziierung von Foo<char,int> . Aber wenn Sie die Teilspezialisierung mit:

versuchen %Vor%

Der Compiler müsste implizit Foo<T,int> vor der Spezialisierung instanziieren, aber wegen T nicht. Und es scheitert.

Sie können das mit dem folgenden Code überprüfen:

%Vor%

Mit g++ gibt es die Fehler:

%Vor%

Mit clang++ ist ein bisschen klarer:

%Vor%     
rodrigo 17.12.2012 23:09
quelle

Tags und Links