2012-12-17 7 views
8

Ich stieß auf einen interessanten Punkt, den ich nicht erklären konnte oder eine Erklärung dafür finden konnte. Betrachten Sie das folgende Template-Definition (kompiliert mit mingw g ++ 4.6.2): ​​Template-Klasse unvollständige Spezialisierung

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 

Wenn wir wollen, können wir voll jedes einzelne Mitglied Funktion spezialisiert:

template <> 
void Foo<char,int>::f() {} 

Aber partielle Spezialisierung nicht mit einem " ungültige Verwendung von unvollständigem Typ ‚Klasse Foo < ...>‘“Fehler:

template <typename T, typename S> 
void Foo<T,S*>::f() 
{ 
} 

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

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.

+0

Fast doppelt: http://stackoverflow.com/questions/12335762/partial-specialization-of-member-function und http://stackoverflow.com/questions/165101/invalid-use-of-incomplete-type- Fehler-mit-Teil-Template-Spezialisierung. – jogojapan

Antwort

6

Der Begriff der Teil Spezialisierung existiert nur für Klassen-Templates (beschrieben von §14.5.5) und Mitglied Vorlagen (dh Mitglieder einer Template-Klasse hilft, die sich Funktionen Vorlage sind, beschrieben durch § 14.5.5.3/2). Es existiert nicht für normale Mitglieder von Klassenvorlagen, noch existiert es für Funktionsschablonen – einfach, weil es nicht durch den Standard beschrieben wird.

Jetzt könnten Sie argumentieren, dass durch die Definition einer Teil Spezialisierung einer Elementfunktion zu geben, wie

template <typename T> 
void Foo<T,int>::f() 
{ } 

Sie implizit eine partielle Spezialisierung der Klassenvorlage definieren: Foo<T,int>.Das aber ist explizit von der Norm ausgeschlossen:

(§14.5.5/2) Each class template partial specialization is a distinct template and definitions shall be provided for the members of a template partial specialization (14.5.5.3).

(§14.5.5.3/1) [...] The members of the class template partial specialization are unrelated to the members of the primary template. Class template partial specialization members that are used in a way that requires a definition shall be defined; the definitions of members of the primary template are never used as definitions for members of a class template partial specialization. [...]

Letzteres bedeutet, dass es unmöglich ist, implizit eine partielle Spezialisierung zu definieren, indem einfach die Definition eines seiner Mitglieder zu geben: Die Existenz dieses Members würde nicht aus der Definition der primären Vorlage folgen, folglich zu definieren, es entspricht definiert eine Member-Funktion, die nicht deklariert wurde, und das ist nicht zulässig (auch mit Nicht-Template-Klassen).

Auf der anderen Seite ist der Begriff expliziten Spezialisierung (oder vollen Spezialisierung, wie Sie es nennen) gibt es für Elementfunktionen von Klassen-Templates. Es wird ausdrücklich von der Norm beschrieben:

(§14.7.3/1) An explicit specialization of any of the following:
[...]
— member function of a class template
[...]
can be declared by a declaration introduced by template<>; [...]

§14.7.3/14 beschreibt die Details:

(§14.7.3/14) A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. [...]

daher für explizite Spezialisierungen der Mitglieder, die Instanziierung des Restes der Klassenvorlage Werke implizit – Es wird von der primären Vorlagendefinition oder von beliebigen partiellen Spezialisierungen abgeleitet, falls sie definiert sind.

+0

Es gibt was ich gesucht habe. Konnte die relevanten Teile des Standards nicht entziffern. Vielen Dank :) – StoryTeller

3

Ich denke, dass der Unterschied, dass, wenn Sie die erste (gültig) explizite Spezialisierung von f tun:

template <> 
void Foo<char,int>::f() {} 

Sie sind eine implizite Instanziierung Foo<char,int> tun. Aber wenn man die partielle Spezialisierung versucht mit:

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

Der Compiler müßte implizit instanziiert Foo<T,int> bevor die Spezialisierung zu tun, aber es kann nicht tun, weil die T. Und es scheitert.

Sie können prüfen, ob der Fall mit dem folgenden Code ist:

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 


template <> 
void Foo<char,int>::f() //line 11 
{} 

template <> 
class Foo<char,int> //line 15 
{}; 

Mit g++ gibt es die Fehler:

test.cpp:15:7: error: specialization of ‘Foo<char, int>’ after instantiation 
test.cpp:15:7: error: redefinition of ‘class Foo<char, int>’ 
test.cpp:2:7: error: previous definition of ‘class Foo<char, int>’ 

Mit clang++ ist etwas klarer:

test.cpp:15:7: error: explicit specialization of 'Foo<char, int>' after instantiation 
class Foo<char,int> 
     ^~~~~~~~~~~~~ 
test.cpp:11:6: note: implicit instantiation first required here 
void Foo<char,int>::f() 
    ^
+0

Danke, das war aufschlussreich :) – StoryTeller

5

Ich habe versucht, ein prägnantes Zitat aus dem Standard zu finden, aber ich denke nicht th Es ist einer. 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.

Lassen Sie uns für eine Sekunde die Vorlagen vergessen. 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 die Klasse also wirklich.

Ein Funktionsname ist andererseits 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 aus wie der Algorithmus zur Auflösung von Funktionsnamen, auf den oben Bezug genommen wurde, aber es gibt signifikante Unterschiede. Einer davon ist, dass Sie im Gegensatz zu Funktionsprototypen nicht zwei Klassenvorlagen mit verschiedenen Arten von Vorlagenparametern haben können.

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

Das ist ein ganz anderer Algorithmus als die Template-Parameterauflösung mit Templaten. Anstatt nur eine Liste der bereitgestellten Template-Argumente mit einer Liste von deklarierten Template-Parametern 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 die Auflösungsspezialisierung zur Überladungsgruppe für eine weitere Runde der Überladungsauflösung hinzu.

Ich denke, es wäre möglich gewesen, auch in diesem Prozess eine teilweise Spezialisierungsauflösung hinzuzufügen, aber die Interaktionen zwischen partieller Spezialisierung und Funktionsüberlastung erscheinen mir wahrscheinlich zu einem pseudo-magischen 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 können.)

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

Normalerweise ist das für Ihre Überlastungsbedürfnisse tatsächlich ausreichend. 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.

Hoffnung, dass ...

+0

Es hilft, sehr viel in der Tat. Danke :) – StoryTeller