2013-05-27 15 views

risposta

13

Ogni specializzazione introduce un tipo di dati completamente nuovo (o un modello completamente nuovo, se la specializzazione è solo parziale). Dalla standard (C++ 11):

(§14.5.5/2) Ciascun modello di classe parziale specializzazione è un modello distinto e definizioni devono essere previsti per i membri di un modello di specializzazione parziale (14.5.5.3).

E:

(§14.5.5.3/1) [...] I membri del modello di classe di specializzazione parziale sono estranei ai membri del modello primario. Devono essere definiti membri di specializzazione parziale del modello di classe che sono utilizzati in un modo che richiede una definizione; le definizioni dei membri del modello primario non vengono mai utilizzate come definizioni per i membri di una specializzazione parziale del modello di classe. [...]

È possibile che questo sia previsto nel contesto di parziali specializzazioni, ma vale anche per le specializzazioni esplicite (come nel tuo caso) e, anche se lo standard non dice molto chiaramente.

Si noti inoltre che è necessario non solo dichiarare tutte le funzioni membro che si desidera in una specializzazione, ma è necessario definirli, anche (qui, lo standard è molto chiara anche su specializzazioni esplicite):

(14.7.3/5) Un membro di una classe esplicitamente specializzata non è istanziato implicitamente dalla dichiarazione membro del modello di classe; al contrario, il membro della specializzazione del modello di classe deve essere definito esplicitamente se la sua definizione è richiesta. In questo caso, la definizione della specializzazione esplicita del modello di classe deve essere nello scope nel punto in cui è definito il membro. La definizione di una classe esplicitamente specializzata non è correlata alla definizione di una specializzazione generata. Cioè, i suoi membri non devono avere gli stessi nomi, tipi, ecc. Come membri di una specializzazione generata. [...]

Così, in effetti, A<int> avrà solo method2(), e A<float> avrà solo method1() come membro. Inoltre, se si introducesse method1() nella specializzazione A<int>, non è necessario avere lo stesso tipo di argomento o il tipo restituito come A<float>::method1().

Vedere la risposta di @ aschepler per possibili modi per evitare di dover riscrivere la definizione del modello per il caso int.

+1

@Fellowshee Sì, puoi farlo. In tal caso, non dichiarare un nuovo 'template <> struct A {...};', ma semplicemente dichiarare (e definire) il metodo che si desidera specializzare: 'A :: method1() {.. .}; 'al di fuori della definizione del modello di classe. – jogojapan

+0

Interessante, una dichiarazione fuori classe e la definizione di una funzione che non è menzionata all'interno della classe è considerata legale? – johnbakers

+0

@Fellowshee Oh scusa, pensavo che avessi bisogno di una specializzazione di 'method1' per il caso' int'. Se hai davvero bisogno di aggiungere un metodo completamente nuovo 'method2' solo per il caso' int', allora in effetti dovrai riscrivere l'intero template per 'int'. A seconda della situazione, potrebbe essere meglio aggiungere 'method2' alla definizione del template primario ma lasciarlo indefinito (o lanciare un'eccezione, o inserire un' static_asssert (false, "non implementato"); '(il quest'ultimo funziona solo in C++ 11)). In questo modo potresti specializzare 'method2' per il caso' int' senza dover riscrivere l'intero modello. – jogojapan

1

La specializzazione sostituisce il modello generico. Quindi A<int> avrà solo method2() e, ovviamente, A<double> avrà solo method1().

+0

Suppongo quindi che il compilatore li tratterà come classi completamente separate senza alcuna relazione tra loro, come se avessero nomi completamente diversi (che probabilmente fanno nella fonte creata?) – johnbakers

+0

Ogni tipo generato da un modello è interamente classe separata senza alcuna relazione con altri tipi generati dallo stesso modello. Questo non fa differenza. 'A ', 'A ' e 'A ' sarebbero tre classi non correlate. – Gorpik

9

@ jogojapan's answer spiega cosa fa il linguaggio.Ecco un paio soluzioni se si vuole aggiungere nuovi membri per una specializzazione specifica:

template<typename T> 
struct A_Base { 
    void method1() {} 
}; 

template<typename T> 
struct A : public A_Base<T> {}; 

template<> 
struct A<int> 
    : public A_Base<int> 
{ 
    void method2() {} 
}; 

Ora A<int> ha membri method1 e method2, ma non ha A<float>method2.

o (se è possibile modificare il modello primario) ...

#include <type_traits> 

template<typename T> 
struct A { 
    void method1() {} 

    template<int N=0> 
    auto method2() -> 
    typename std::enable_if<std::is_same<T, int>::value && N==N>::type 
    {} 
}; 

I template<int N> e N==N parti assicurarsi std::enable_if ha un valore dipendente e, pertanto, non si lamenta fino a quando qualcuno in realtà cerca di utilizzare A<T>::method2 con un parametro errato T.

+0

+1, esp. per la tecnica enable-if. – jogojapan

+0

@aschepler - La prima tecnica (non la tecnica C++ 11) funzionerebbe se 'A_Base' fosse * non * un modello? Cioè, puoi avere un 'A :: A_Base :: method2'? Sto ottenendo un errore da MSVC e non ho mai visto l'errore prima ([C2936] (https://msdn.microsoft.com/en-us/library/ctfzt48x.aspx)). Non sono sicuro che non sia possibile, non è possibile con C++ 03, o se è una limitazione MSVC. – jww

+0

@jww Sì, dovrebbe funzionare. Non sono sicuro del motivo per cui potresti ottenere questo errore. La pagina che hai collegato suggerisce di verificare la presenza di parentesi graffe non corrispondenti. Se ancora non vedi uno stupido errore, potresti voler scrivere e testare un [mcve] e postare una nuova domanda al riguardo. – aschepler

Problemi correlati