2014-01-08 4 views
7

Ich habe den folgenden Code:Warum stimmt die Definition der allgemeinen Vorlagenmethode nicht mit der Vorlagenklassenspezialisierung überein?

template <class T, class U = T> 
class A { 
    public: 
    void f(); 
}; 

template <class T> 
class A<T, T> { 
    public: 
    void f(); // Unaltered method. 

    // Some differences. 
}; 

template <class T, class U> 
void A<T, U>::f() {} 

int main() { 
    A<int> a; 
    a.f(); 
    return 0; 
} 

Die clang++ -std=c++11 test.cc gibt mir eine Fehlermeldung: undefined reference to 'A<int, int>::f()'

Warum die bereitgestellte Definition der Methode f() nicht auf die Klasse gilt A<int, int>?

+0

Whare sind die Unterschiede zwischen A und A ? Möglicherweise können Sie eine Spezialisierung der Klasse vermeiden (mit enable_if, SFINAE ...). – ysdx

+0

@ysdx Der Unterschied liegt in der Deklaration einer lokalen Klasse und deren Verwendung in einigen Methoden. –

Antwort

6

Die primäre Klassenvorlage template <class T, class U = T> class A und die Teilspezialisierung template <class T> class A<T, T> sind zwei unterschiedliche Vorlagendefinitionen. Nachdem sie definiert wurden, werden immer dann, wenn Sie auf den Klassenvorlagennamen A verweisen, die primäre Vorlage und alle Teilspezialisierungen immer berücksichtigt.

Immer wenn Sie A entweder mit einem einzelnen Vorlagenargument oder zwei Argumenten desselben Typs instanziieren, wird es besser zu der von Ihnen bereitgestellten Spezialisierung passen, und die primäre Vorlage wird nicht berücksichtigt.

In Ihrem Beispiel gibt es aufgrund der von Ihnen bereitgestellten Teilspezialisierung keine Möglichkeit, die primäre Vorlage ungeachtet des Standardschablonenarguments abzugleichen, wenn Sie versuchen, A mit einem einzigen Vorlagenargument oder zwei davon zu instanziieren dieselbe Art.

Die Lösung ist natürlich, ist die Definition für A<T, T>::f()

template <class T> 
void A<T, T>::f() {} 

EDIT bereitzustellen:
In Gegenwart von Teil Spezialisierungen, werden die Regeln für den Abgleich von ihnen (von N3797) §14.5 gegeben. 5.1/1 [temp.class.spec.match]

When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
— If exactly one matching specialization is found, the instantiation is generated from that specialization.
— If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine whether one of the specializations is more specialized than the others. ...
— If no matches are found, the instantiation is generated from the primary template.

In Ihrem Beispiel die erste Regel gilt, und der Compiler nicht einmal in der 3. Regel bekommt.

+0

Danke für die Antwort, aber es wäre großartig, wenn Sie mehr Details darüber geben, warum meine Definition vom Compiler nicht berücksichtigt wird - soweit ich weiß, ist die Definition eine eigene Vorlage, die auf beide Klassenvorlagen abgestimmt werden sollte. Was vermisse ich? –

+0

@ abyss.7 Ich habe auf die Antwort aktualisiert. Die kurze Antwort ist, weil die Sprachregeln sagen, dass nur die partielle Spezialisierung übereinstimmt, und nicht die primäre. – Praetorian

+0

Ich verstehe es nicht, aber ich möchte wirklich: Wenn die Klassenvorlage Instanziierung in Zeilen 'Vorlage void A :: f() {} 'erforderlich ist - dann sollte es übereinstimmen' void A :: f() {} ', dh die Spezialisierung der Klassenvorlage, und es sollte kein Problem geben. Vielleicht können wir ein bisschen plaudern? –

1

Wenn Sie eine Memberfunktion einer Klassenvorlage außerhalb der Klasse definieren, definieren Sie nur die Funktion für die entsprechende Funktion, die in der Klassenvorlage deklariert wurde. Sie erstellen keine neue Funktionsvorlage, die anderen Parametern entsprechen könnte. In Ihrem Beispiel:

template <class T, class U = T> 
class A { 
    public: 
    void f(); // This is the declaration of A<T,U>::f() 
}; 

template <class T> 
class A<T, T> { 
    public: 
    void f(); // This is the declaration of A<T,T>::f() 
}; 

template <class T, class U> 
void A<T, U>::f() {} // This is the definition of A<T,U>::f() 

// There is no definition of A<T,T>::f() 

glaube ich, was Sie denken ist, dass der Compiler werden sehen, dass Sie A<int,int>::f() fordern und die Mitgliedsfunktionsdefinitionen durchsehen und finden, das passt, aber das ist nicht, was passiert. Der Compiler durchsucht immer die Klassenvorlagen, um zu ermitteln, welche Funktion aufgerufen werden soll. Sobald er eine Übereinstimmung gefunden hat, sucht er nach der entsprechenden Definition. In Ihrem Fall rufen Sie A<int,int>::f(), so dass es zuerst nach einer Klassendefinition sucht, die A<int,int> entspricht, und es findet Ihre A<T,T> Klassenvorlagenspezialisierung. Es sieht, dass A<T,T> tatsächlich eine Elementfunktion namens f hat, die Ihrem Funktionsaufruf entspricht, was bedeutet, dass A<T,T>::f() instanziiert werden muss. Um A<T,T>::f() zu instanziieren, sucht der Compiler nach der Definition A<T,T>::f(), jedoch findet es es nicht. Es findet nur die Definition A<T,U>::f, die keine Übereinstimmung ist. Der passende Template-Parameter, der verwendet wird, um eine korrekte Funktionsdeklaration zu finden, trifft nicht zu.

Verwandte Themen