2012-12-17 22 views
8

Mi sono imbattuto in un punto interessante che non ero in grado di spiegare o trovare una spiegazione. Si consideri la seguente definizione del modello (compilato con MinGW g ++ 4.6.2): ​​Specializzazione incompleta classe modello

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

caso vogliamo, siamo in grado di specializzarsi completamente ogni singola funzione membro:

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

Ma parziale specializzazione fallisce con un " utilizzo non valido di tipo incompleto 'classe Foo < ...>'" errore:

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

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

E non riesco a capire perché. È una decisione progettuale consapevole presa per evitare qualche problema che non posso prevedere? È una svista?

Grazie in anticipo.

+0

Quasi duplicato: http://stackoverflow.com/questions/12335762/partial-specialization-of-member-function e http://stackoverflow.com/questions/165101/invalid-use-of-incomplete-type- error-con-parziale-template-specializzazione. – jogojapan

risposta

6

La nozione di specializzazione parziale esiste solo per i modelli di classe (descritti da §14.5.5) e modelli di membro (vale a dire i membri di una classe template che sono essi stessi funzioni template, descritto da §14.5.5.3/2). Non esiste per i membri ordinari dei modelli di classe, né esiste per i modelli di funzione – semplicemente perché non è descritto dallo standard.

Ora, si potrebbe sostenere che, dando la definizione di una specializzazione parziale di una funzione membro, come ad esempio

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

si implicitamente definire una specializzazione parziale del modello di classe: Foo<T,int>.Che, tuttavia, è esplicitamente escluso dalla Norma:

(§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. [...]

Quest'ultimo implica che è impossibile implicitamente definiscono una specializzazione parziale, semplicemente dando la definizione di uno dei suoi membri: L'esistenza stessa di quel membro non seguirebbe dalla definizione del modello primario, quindi definendolo è equivalente a che definisce una funzione membro che non era dichiarata e che non è consentita (anche con classi non template).

D'altra parte, la nozione di esplicito specializzazione (o specializzazione completa, come la chiama lei) esiste per le funzioni di membro di modelli di classe. E 'esplicitamente descritte dalle norme:

(§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 descrive i dettagli:

(§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. [...]

Quindi, per specializzazioni esplicite di membri, la creazione di istanze di tutto il resto delle opere di modello di classe implicitamente – è derivato dalla definizione del modello principale o da eventuali specializzazioni parziali se definite.

+0

C'è quello che stavo cercando. Non è stato in grado di decifrare le parti rilevanti dello standard. Grazie :) – StoryTeller

3

Penso che la differenza è che quando si fa il primo (valido) specializzazione esplicita di f:

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

State facendo un'esemplificazione implicita di Foo<char,int>. Ma quando si tenta la specializzazione parziale:

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

Il compilatore avrebbe bisogno di istanziare implicitamente Foo<T,int> prima di fare la specializzazione, ma non può farlo a causa della T. E fallisce.

È possibile verificare che è il caso con il seguente codice:

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 
{}; 

Con g++ dà gli errori:

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>’ 

Con clang++ è un po 'più chiaro:

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

Grazie, è stato perspicace :) – StoryTeller

5

Ho cercato di trovare una citazione succinta dallo standard, ma non penso che prima è uno. Il fatto è che non esiste una specializzazione parziale di una funzione template (o, peraltro, di un alias template). Solo i modelli di classe possono avere specializzazioni parziali.

Dimentichiamo i modelli per un secondo. In C++, c'è una grande differenza tra nomi di classi e nomi di funzioni. Può esserci solo una definizione di una classe all'interno di un determinato ambito. (Puoi avere varie dichiarazioni, ma si riferiscono tutte alla One True Class.) Quindi il nome identifica davvero la classe.

Un nome di funzione, d'altra parte, è un tipo di identità di gruppo. È possibile definire qualsiasi numero di funzioni all'interno di un ambito con esattamente lo stesso nome. Quando si utilizza un nome di funzione per chiamare una funzione, il compilatore deve capire quale funzione si intendesse realmente, osservando le varie possibilità e facendo corrispondere la firma di ognuna di esse con gli argomenti forniti. Non c'è alcuna relazione tra le varie funzioni che condividono un nome; sono entità completamente separate.

Quindi, nessun grosso problema. Sapevi tutto questo, vero? Ma ora torniamo ai modelli.

Il nome di una classe basata su modelli è ancora univoco.Sebbene sia possibile definire specializzazioni parziali, è necessario specializzarsi esplicitamente nella stessa classe basata su modelli. Questo meccanismo sembra superficialmente simile all'algoritmo di risoluzione del nome di funzione di cui sopra, ma ci sono differenze significative - una di queste è che, a differenza dei prototipi di funzione, non è possibile avere due modelli di classe nello stesso ambito con diversi tipi di parametri del modello.

Le funzioni basate su modelli, d'altra parte, non hanno bisogno di definire nomi univoci. I modelli non sostituiscono il normale meccanismo di sovraccarico delle funzioni. Quindi, quando il compilatore sta cercando di capire cosa significhi un nome di funzione, deve prendere in considerazione tutte le dichiarazioni basate su modelli e senza modelli per quel nome di funzione, risolvere i modelli su un insieme di assegnazioni dei parametri del modello (se possibile) e poi una volta ha una lista di possibili oggetti funzione, seleziona la migliore con normale risoluzione di sovraccarico.

Questo è un algoritmo piuttosto diverso dalla risoluzione dei parametri del modello di classe con modelli. Invece di abbinare semplicemente una lista di argomenti template forniti con una lista di parametri template dichiarati, che è come risolve i template di classe, deve prendere ogni funzione basata su template che potrebbe eventualmente corrispondere (ha almeno il giusto numero di parametri, per esempio) ; dedurre i parametri del modello unificando gli argomenti forniti con il modello; e quindi aggiungere la specializzazione di risoluzione al set di sovraccarico per un ulteriore ciclo di risoluzione del sovraccarico.

Suppongo che sarebbe stato possibile aggiungere una risoluzione di specializzazione parziale anche a quel processo, ma le interazioni tra specializzazione parziale e sovraccarico di funzioni mi sembrano più propensi a condurre a comportamenti pseudo-magici. Nel caso, non era necessario e quindi non esiste un tale meccanismo. (È possibile specializzarsi completamente un modello di funzione di specializzazione completa significa che non ci sono argomenti template a dedurre, quindi non è un problema..)

Ecco, questo è lo scoop: non è possibile specializzarsi in parte una funzione di template, ma c'è nulla ti impedisce di fornire un numero qualsiasi di modelli di funzioni con lo stesso nome. Tutti saranno considerati in risoluzione di sovraccarico, e il migliore vincerà, come al solito.

Di solito, questo è effettivamente sufficiente per le vostre esigenze di sovraccarico. Dovresti pensare alle funzioni basate su modelli allo stesso modo in cui pensi alle normali funzioni: scegli un modo per selezionare quello che vuoi in base agli argomenti forniti. Se ritieni che sia necessario fornire parametri di template in una chiamata di funzione, piuttosto che averli dedotti, basta rendere la funzione un membro (possibilmente statico) di una classe basata su modelli e fornire gli argomenti del template alla classe.

Speranza che aiuta ...

+0

Aiuta molto, anzi. Grazie :) – StoryTeller

Problemi correlati