2014-11-13 16 views
5

devo seguenti classi:C++ 11 Template Method specializzazione per tipo di ritorno

class Foo { 
public: 
    template <typename T> 
    T bar() { 
     cout << "Called with return type: " << typeid(T).name() << endl; 

     T t = //... (some implementation here) 

     return t; 
    } 
} 

viene invocato in modo seguente:

Foo foo; 
int i = foo.bar<int>(); 
long l = foo.bar<long>(); 

ora mi piacerebbe avere diversa specializzazione per casi in cui la funzione viene richiamata con shared_ptr<T>

Foo foo; 
foo.bar<shared_ptr<int>>(); 
foo.bar<shared_ptr<long>>(); 

Ma naturalmente io non voglio creare full spec ializzazione per ogni tipo. È possibile implementare tale comportamento (può essere basato sui caratteri se necessario)?

+2

I modelli di funzione non possono essere parzialmente specializzati, ma i modelli di classe possono. Creare un modello di classe wrapping che espone una funzione membro statica pubblica, specializzare parzialmente la classe e modificare la funzione membro in modo appropriato. – 0x499602D2

risposta

4

Poiché nessuno ha proposto ancora, si può usare SFINAE di distinguere tra T e std::shared_ptr<U>:

template <typename T> 
struct is_shared_ptr_impl : std::false_type {}; 
template <typename T> 
struct is_shared_ptr_impl<std::shared_ptr<T>> : std::true_type {}; 
template <typename T> 
using is_shared_ptr = typename is_shared_ptr_impl<typename std::decay<T>::type>::type; 

class Foo 
{ 
public:  
    template <typename T> 
    auto bar() 
     -> typename std::enable_if<!is_shared_ptr<T>{}, T>::type 
    { 
     std::cout << "T is " << typeid(T).name() << std::endl; 
     return {}; 
    } 

    template <typename T> 
    auto bar() 
     -> typename std::enable_if<is_shared_ptr<T>{}, T>::type 
    { 
     using U = typename std::decay<T>::type::element_type; 
     std::cout << "T is shared_ptr of " << typeid(U).name() << std::endl; 
     return {}; 
    } 
}; 

DEMO

+0

Non si prendono in considerazione le qualifiche CV. Che dire di rendere 'is_shared_ptr'' is_shared_ptr_impl' e 'is_shared_ptr' un modello alias che si riferisce a' is_shared_ptr_impl :: type> '? – Columbo

+0

@Columbo che ne dici adesso? –

+0

Non ho pensato ai riferimenti. Ora va bene. – Columbo

6

Ci sono sicuramente molti modi per farlo. Il primo modo che mi viene in mente è semplicemente l'overloading delle funzioni. Dal momento che non hai un parametro per sovraccaricare, dovrai crearne uno. Mi piacciono i puntatori, che agiscono efficacemente come un modo per passare i tipi alle funzioni.

class Foo { 

    //regular overload 
    template<typename T> 
    T bar(T*) { //takes a pointer with an NULL value 
     cout << "Called with return type: " << typeid(T).name() << endl; 

     T t = //... (some implementation here) 

     return t; 
    } 
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR 
    template<typename T> 
    std::shared_ptr<T> bar(std::shared_ptr<T>*) { //takes a pointer with an null value 
     cout << "Called with return type: " << typeid(T).name() << endl; 

     std::shared_ptr<T> t = //... (some implementation here) 

     return t; 
    } 

public: 
    template <typename T> 
    T bar() { 
     T* overloadable_pointer = 0; 
     return bar(overloadable_pointer); 
    } 
}; 

non ho mai sentito parlare di chiunque altro l'utilizzo di puntatori far passare tipi in giro, quindi se si sceglie di fare questo, di commentare a fondo, giusto per essere sicuri. È è codice strano.

Potrebbe essere più intuitivo utilizzare semplicemente una struttura di supporto per la specializzazione dei modelli, che è ciò che farebbero la maggior parte delle persone. Sfortunatamente, se hai bisogno di accedere ai membri di Foo (cosa che presumibilmente fai), usare la specializzazione del modello richiede di passare tutti quei membri alla funzione o di essere amici degli aiutanti del modello. In alternativa, potresti passare una cosa di specializzazione type_traits a un altro membro, ma questo finisce semplicemente per essere una versione complessa del trucco del puntatore sopra. Molti lo trovano più normale e meno confuso, quindi ecco:

template<typename T> 
struct Foo_tag {}; 

class Foo { 
    //regular overload 
    template<typename T> 
    T bar(Foo_tag<T>) { 
    } 
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR 
    template<typename T> 
    std::shared_ptr<T> bar(Foo_tag<std::shared_ptr<T>>) { 
    } 

public: 
    template <typename T> 
    T bar() { 
     return bar(Foo_tag<T>{}); 
    } 
} 
+1

Questo è un modo interessante per farlo. – 0x499602D2

+0

Effettivamente, ci proverò :) – Daimon

+0

Perché "con un valore non definito"? Il valore è 0 ... intendi solo che è senza nome vero? – Barry

8

Non è possibile specializzare parzialmente le funzioni. Per una storia sul perché, dai un'occhiata a questo GOTW.

È può parzialmente specializzarsi classi, però, così quello che si potrebbe fare è:

template <typename T> 
T bar() { 
    return bar_impl<T>::apply(this); 
} 

Dove:

template <typename T> 
struct bar_impl { 
    static T apply(Foo*) { 
     // whatever 
    } 
} 

template <typename T> 
struct bar_impl<std::shared_ptr<T>> { 
    static std::shared_ptr<T> apply(Foo*) { 
     // whatever else 
    } 
} 
+0

Sembra essere la soluzione che mi è venuta in mente dopo il commento 0x499602D2. Inoltre sembra un po 'meno "hacky" della risposta di Mooing Duck – Daimon

+0

@Daimon, non so se lo definirei "hacky" necessariamente. Non l'ho mai visto prima e offre almeno due vantaggi rispetto alla mia risposta: (1) tutto il codice 'Foo' è in' Foo' (2) è possibile accedere ai membri privati ​​di 'Foo' senza amicizia. Il mio è solo il modo più accettato/comune per farlo. – Barry

+0

Riguardo all'amicizia. Non posso semplicemente rendere bar_impl una classe Inner di Foo? Questo dovrebbe creare automaticamente l'amicizia e continuare a mantenere l'implementazione internamente? – Daimon

Problemi correlati