2013-02-17 23 views
10

Ho un modello di classe che io uso per ottenere la dimensione di una variabile:Const e modello non const specializzazione

template <class T> 
class Size 
{ 
    unsigned int operator() (T) {return sizeof(T);} 
}; 

Questo funziona bene, ma per archi voglio usare strlen invece di sizeof:

template <> 
class Size<char *> 
{ 
    unsigned int operator() (char *str) {return strlen(str);} 
}; 

Il problema è quando creo un'istanza di dimensione con const char * passa alla versione non specializzata. Mi chiedevo se c'è un modo per catturare sia la versione const sia quella non-const di char * nella specializzazione template? Grazie.

+1

Io non sono convinto che questo sia un buona idea. Che dire del caso in cui hai un '(const) char *' che non punta a una stringa? –

+0

Funziona solo per specializzare la versione 'const'? – Bingo

+0

No quando si passa un carattere non const * non usa la specializzazione. –

risposta

9

utilizzare questa tecnica:

#include <type_traits> 

template< typename T, typename = void > 
class Size 
{ 
    unsigned int operator() (T) {return sizeof(T);} 
}; 

template< typename T > 
class Size< T, typename std::enable_if< 
       std::is_same< T, char* >::value || 
       std::is_same< T, const char* >::value 
       >::type > 
{ 
    unsigned int operator() (T str) { /* your code here */ } 
}; 

EDIT: esempio di come definire i metodi di fuori della definizione della classe.

EDIT2: Aggiunto helper per evitare di ripetere la condizione possibilmente lunga e complessa.

EDIT3: Assistente semplificato.

#include <type_traits> 
#include <iostream> 

template< typename T > 
struct my_condition 
    : std::enable_if< std::is_same< T, char* >::value || 
        std::is_same< T, const char* >::value > 
{}; 

template< typename T, typename = void > 
struct Size 
{ 
    unsigned int operator() (T); 
}; 

template< typename T > 
struct Size< T, typename my_condition<T>::type > 
{ 
    unsigned int operator() (T); 
}; 

template< typename T, typename Dummy > 
unsigned int Size< T, Dummy >::operator() (T) 
{ 
    return 1; 
} 

template< typename T > 
unsigned int Size< T, typename my_condition<T>::type >::operator() (T) 
{ 
    return 2; 
} 

int main() 
{ 
    std::cout << Size<int>()(0) << std::endl; 
    std::cout << Size< char* >()(0) << std::endl; 
    std::cout << Size< const char* >()(0) << std::endl; 
} 

che stampa

1 
2 
2 
+1

Grazie, non conosco la sintassi 'typename = void', come definirebbe operator() se voglio scrivere il corpo della funzione al di fuori della classe e non inline, in entrambi i casi? –

+1

È appena abbreviato per 'typename Dummy = void' dove' Dummy' non viene mai usato. Controllerò e migliorerò la risposta su come definire 'operator()' al di fuori della classe ... –

+0

Grazie e per il caso specifico ho bisogno di scrivere tutta questa cosa lunga nella definizione della funzione? –

4

Un modo è quello di utilizzare una funzione di supporto al fine di determinare se il tipo di modello è un char * o un char const *. Puoi farlo con una semplice struttura. Quindi è possibile utilizzare SFINAE per selezionare la specializzazione appropriata della propria struttura Size.

#include <cstring> 
#include <iostream> 

#include <boost/utility/enable_if.hpp> 

template<typename T> 
struct is_cstring { 
    enum { value = false }; 
}; 

template<> 
struct is_cstring<char *> { 
    enum { value = true }; 
}; 

template<> 
struct is_cstring<char const *> { 
    enum { value = true }; 
}; 

template<typename T, typename = void> 
struct Size; 

template<typename T> 
struct Size<T, typename boost::disable_if<is_cstring<T> >::type> { 
    unsigned int operator()(T const &) const { 
    return sizeof(T); 
    } 
}; 

template<typename T> 
struct Size<T, typename boost::enable_if<is_cstring<T> >::type> { 
    unsigned int operator()(T const &str) const { 
    return strlen(str); 
    } 
}; 

int main() { 
    std::string blah = "afafasa"; 
    char *x = "asdfsadsad"; 

    std::cout << Size<int>()(4) << std::endl; 
    std::cout << Size<char const *>()("blahblah") << std::endl; 
    std::cout << Size<char *>()(x) << std::endl; 
} 

Il risultato stampato è:

4 
8 
10 
-1

La soluzione più semplice è sostituire la char * specializzazione con const char * uno. Funzionerà anche per char *.

template <> 
class Size<const char *> 
{ 
    unsigned int operator() (const char *str) {return strlen(str);} 
}; 
+0

-1 Se qualcuno usa 'Dimensione :: operator()', non userà la versione specializzata che hai mostrato. –

0

E si dovrebbe anche essere in grado di scrivere, naturalmente:

template <> 
class Size<const char *> 
{ 
    unsigned int operator() (const char *str) {return strlen(str);} 
}; 

template <> 
class Size<char *> : public Size<const char *> 
{ }; 

... e, se avete bisogno di:

template <size_t size> 
class Size<char[size]> : public Size<const char *> 
{ }; 

template <size_t size> 
class Size<const char[size]> : public Size<const char *> 
{ };