58

So che il codice qui sotto è una specializzazione parziale di una classe:Specializzazione parziale del modello di funzione C++?

template <typename T1, typename T2> 
class MyClass { 
    … 
}; 


// partial specialization: both template parameters have same type 
template <typename T> 
class MyClass<T,T> { 
    … 
}; 

Inoltre so che C++ non permette funzione di modello di specializzazione parziale (solo pieno è consentito). Ma il mio codice significa che ho parzialmente specializzato il mio modello di funzione per argomenti dello stesso tipo? Perché funziona con Microsoft Visual Studio 2010 Express! Se no, allora potresti spiegare il concetto di specializzazione parziale?

#include <iostream> 
using std::cin; 
using std::cout; 
using std::endl; 

template <typename T1, typename T2> 
inline T1 max (T1 const& a, T2 const& b) 
{ 
    return a < b ? b : a; 
} 

template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return 10; 
} 


int main() 
{ 
    cout << max(4,4.2) << endl;; 
    cout << max(5,5) << endl; 
    int z; 
    cin>>z; 
} 
+0

cercare quel analogia della classe di specializzazione. Se si chiama specializzazione di classe, allora perché dovrei considerare la stessa cosa per la funzione di sovraccarico ?? – Narek

+0

No, la sintassi di specializzazione è diversa. Guarda la (supposta) sintassi di specializzazione della funzione nella mia risposta qui sotto. – iammilind

+1

Perché questo non genera un errore "Call to max is ambigious"? Come 'max (5,5)' risolve in 'max (T const &, T const &) [con T = int]' e non 'max (T1 const &, T2 const &) [con T1 = int e T2 = int]' ? – NHDaly

risposta

58

Nell'esempio, si è in realtà sovraccarico (non specializzato) la funzione max<T1,T2>. specializzazione parziale sintassi dovrebbe avere guardato po come qui di seguito (qualora fosse stato permesso):

//Partial specialization is not allowed by the spec, though! 
template <typename T> 
inline T const& max<T,T> (T const& a, T const& b) 
{     ^^^^^ <--- specializing here 
    return 10; 
} 

[Nota: nel caso di un modello di funzione, solo piena specializzazione è consentito dalla lo standard C++ (escluse le estensioni del compilatore).]

+0

Non è permesso perché potrebbe confondere il compilatore per confondere la versione specializzata giusta della funzione, no? Se è vero, ciò che sarà confuso per il compilatore se la funzione non è stata sovraccaricata dal modo in cui ho scritto, ma è stata specializzata dal modo in cui hai scritto (in modo non consentito)? – Narek

+1

@Narek, la specializzazione della funzione parziale non fa parte dello standard (per qualsiasi motivo). Penso che MSVC lo supporti come un'estensione. Potrebbe essere dopo un po ', sarebbe consentito anche da altri compilatori. – iammilind

+0

@iammilind: modificato il post e +1. – Nawaz

4

No. Per esempio, si può legalmente specializzarsi std::swap, ma non si può legalmente definire il proprio sovraccarico. Ciò significa che non è possibile eseguire std::swap per il proprio modello di classe personalizzato.

Il sovraccarico e la specializzazione parziale possono avere lo stesso effetto in alcuni casi, ma lontano da tutti.

+4

Ecco perché inserisci il sovraccarico di 'swap' nel tuo spazio dei nomi. – jpalecek

12

Che cos'è la specializzazione?

Se vuoi veramente capire i modelli, dovresti dare un'occhiata ai linguaggi funzionali. Il mondo dei template in C++ è un sottolinguaggio puramente funzionale.

In linguaggi funzionali, le selezioni sono fatte usando Pattern Matching:

-- An instance of Maybe is either nothing (None) or something (Just a) 
-- where a is any type 
data Maybe a = None | Just a 

-- declare function isJust, which takes a Maybe 
-- and checks whether it's None or Just 
isJust :: Maybe a -> Bool 

-- definition: two cases (_ is a wildcard) 
isJust None = False 
isJust Just _ = True 

Come potete vedere, abbiamo sovraccarico la definizione di isJust.

Bene, i modelli di classe C++ funzionano esattamente allo stesso modo. Fornisci una dichiarazione principale , che indica il numero e la natura dei parametri. Può essere solo una dichiarazione, o anche una definizione (a tua scelta), e quindi puoi (se lo desideri) fornire delle specializzazioni del modello e associarle a una versione diversa (altrimenti sarebbe sciocca) della classe .

Per le funzioni di modello, la specializzazione è un po 'più scomoda: è in qualche modo in conflitto con la risoluzione di sovraccarico. Come tale, è stato deciso che una specializzazione si riferirebbe a una versione non specializzata, e le specializzazioni non sarebbero considerate durante la risoluzione di sovraccarico.Pertanto, l'algoritmo per la selezione della funzione di destra diventa:

  1. eseguire la risoluzione di sovraccarico, tra funzioni regolari e modelli non specializzati
  2. Se si seleziona un modello non specializzato, controllare se una specializzazione esiste per essa che avrebbe essere una migliore corrispondenza

(per il trattamento su approfonditi, vedi GotW #49)

come tale, modello specializzazione delle funzioni è un cittadino seconda zona (letteralmente). Per quanto mi riguarda, staremmo meglio senza di loro: devo ancora incontrare un caso in cui un uso della specializzazione del modello non può essere risolto con sovraccarico.

È questa una specializzazione di modello?

No, è semplicemente un sovraccarico, e questo va bene. In effetti, i sovraccarichi di solito funzionano come ci aspettiamo, mentre le specializzazioni possono essere sorprendenti (ricorda l'articolo di GotW che ho linkato).

+0

'" In quanto tale, la specializzazione del modello delle funzioni è un cittadino di seconda zona (letteralmente). Per quanto mi riguarda, staremmo meglio senza di loro: devo ancora incontrare un caso in cui un uso della specializzazione del modello non potrebbe essere risolto invece con un sovraccarico. "Che ne dici di parametri modello non di tipo? – Julius

+0

@Julius: puoi ancora utilizzare l'overloading, anche se introducendo un parametro dummy come 'boost :: mpl :: integral_c '. Un'altra soluzione potrebbe anche essere l'uso di 'enable_if' /' disable_if', anche se è una storia diversa. –

28

Dal specializzazione parziale non è consentito - come altre risposte hanno sottolineato -, si potrebbe lavorare intorno ad esso utilizzando std::is_same e std::enable_if, come di seguito:

template <typename T, class F> 
inline typename std::enable_if<std::is_same<T, int>::value, void>::type 
typed_foo(const F& f) { 
    std::cout << ">>> messing with ints! " << f << std::endl; 
} 

template <typename T, class F> 
inline typename std::enable_if<std::is_same<T, float>::value, void>::type 
typed_foo(const F& f) { 
    std::cout << ">>> messing with floats! " << f << std::endl; 
} 

int main(int argc, char *argv[]) { 
    typed_foo<int>("works"); 
    typed_foo<float>(2); 
} 

uscita:

$ ./a.out 
>>> messing with ints! works 
>>> messing with floats! 2 

Modifica: Se è necessario essere in grado di trattare tutti gli altri casi rimasti, è possibile aggiungere una definizione che indica che i casi già trattati non dovrebbero corrispondere a corrispondere a - altrimenti ci si ritroverà in definizioni ambigue. La definizione potrebbe essere:

template <typename T, class F> 
inline typename std::enable_if<(not std::is_same<T, int>::value) 
    and (not std::is_same<T, float>::value), void>::type 
typed_foo(const F& f) { 
    std::cout << ">>> messing with unknown stuff! " << f << std::endl; 
} 

int main(int argc, char *argv[]) { 
    typed_foo<int>("works"); 
    typed_foo<float>(2); 
    typed_foo<std::string>("either"); 
} 

che produce:

$ ./a.out 
>>> messing with ints! works 
>>> messing with floats! 2 
>>> messing with unknown stuff! either 

Anche se questo tutti-i casi cosa sembra un po 'noioso, dal momento che si deve dire tutto compilatore che hai già fatto, è abbastanza fattibile per trattare fino a 5 o più specializzazioni.

+0

Non c'è davvero bisogno di fare ciò poiché questo può essere gestito dall'overloading delle funzioni in modo molto più semplice e chiaro. – Adrian

+2

@Adrian Davvero non riesco a pensare ad alcun altro metodo di overloading per risolvere questo problema. Hai notato che il sovraccarico parziale non è permesso, giusto? Condividi con noi la tua soluzione, se pensi che sia più chiara. – Rubens

+1

c'è un altro modo di fare ** facilmente ** catturare tutte le funzioni di modello? – Nick

0

Risposta tardiva, ma alcuni lettori recenti potrebbero trovare utile: A volte, una funzione di supporto – progettata in modo che possa essere specializzata – può risolvere il problema, anche.

Quindi cerchiamo di immaginare, questo è quello che abbiamo cercato da risolvere:

template <typename R, typename X, typename Y> 
void function(X x, Y y) 
{ 
    R* r = new R(x); 
    f(r, y); // another template function? 
} 

// for some reason, we NEED the specialization: 
template <typename R, typename Y> 
void function<R, int, Y>(int x, Y y) 
{ 
    // unfortunately, Wrapper has no constructor accepting int: 
    Wrapper* w = new Wrapper(); 
    w->setValue(x); 
    f(w, y); 
} 

OK, parziale funzione di modello di specializzazione, non possiamo farlo ... Quindi cerchiamo di "esportare" la parte necessaria per la specializzazione in una funzione di supporto, specializzati che uno e usarlo:

template <typename R, typename T> 
R* create(T t) 
{ 
    return new R(t); 
} 
template <> 
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal... 
{ 
    Wrapper* w = new Wrapper(); 
    w->setValue(n); 
    return w; 
} 

template <typename R, typename X, typename Y> 
void function(X x, Y y) 
{ 
    R* r = create<R>(x); 
    f(r, y); // another template function? 
} 

Questo può essere interessante, soprattutto se le alternative (normali sovraccarichi invece di specializzazioni, la workaround proposto da Rubens, ...– non è che questi sono cattivi o il mio è meglio, solo un altro uno) condividerebbe un bel po 'di codice comune.

2

non-classe, specializzazione parziale non variabile non è consentito, ma come detto:

Tutti i problemi in calcolatore science possono essere risolti altro livello di riferimento indiretto. - David Wheeler

Aggiunta di una classe per inoltrare la chiamata alla funzione in grado di risolvere questo, ecco un esempio:

template <class Tag, class R, class... Ts> 
struct enable_fun_partial_spec; 

struct fun_tag {}; 

template <class R, class... Ts> 
constexpr R fun(Ts&&... ts) { 
    return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
     std::forward<Ts>(ts)...); 
} 

template <class R, class... Ts> 
struct enable_fun_partial_spec<fun_tag, R, Ts...> { 
    constexpr static R call(Ts&&... ts) { return {0}; } 
}; 

template <class R, class T> 
struct enable_fun_partial_spec<fun_tag, R, T, T> { 
    constexpr static R call(T, T) { return {1}; } 
}; 

template <class R> 
struct enable_fun_partial_spec<fun_tag, R, int, int> { 
    constexpr static R call(int, int) { return {2}; } 
}; 

template <class R> 
struct enable_fun_partial_spec<fun_tag, R, int, char> { 
    constexpr static R call(int, char) { return {3}; } 
}; 

template <class R, class T2> 
struct enable_fun_partial_spec<fun_tag, R, char, T2> { 
    constexpr static R call(char, T2) { return {4}; } 
}; 

static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, ""); 
static_assert(fun<int>(1, 1) == 2, ""); 

static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, ""); 
static_assert(fun<char>(1, 1) == 2, ""); 

static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, ""); 
static_assert(fun<long>(1L, 1L) == 1, ""); 

static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, ""); 
static_assert(fun<double>(1L, 1L) == 1, ""); 

static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, ""); 
static_assert(fun<int>(1u, 1) == 0, ""); 

static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, ""); 
static_assert(fun<char>(1, 'c') == 3, ""); 

static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, ""); 
static_assert(fun<unsigned>('c', 1) == 4, ""); 

static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, ""); 
static_assert(fun<unsigned>(10.0, 1) == 0, ""); 

static_assert(
    std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, ""); 
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, ""); 

static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, ""); 
static_assert(fun<unsigned>() == 0, ""); 
+0

Elegante! Questa dovrebbe essere la risposta accettata IMHO. – ulatekh

Problemi correlati