5

Supponiamo di avere la seguente classe templateAiuto con caratteri morfologici

template<typename T> class Wrap { /* ... */ }; 

Noi non possiamo cambiare Wrap. È importante.

Lasciate che ci siano classi derivate da Wrap<T>. Ad esempio,

class NewInt : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 
class Foo  : public Wrap<Bar>  { /* ... */ }; 

Noi non può cambiare queste classi troppo. Tutte le classi sopra sono di terze parti. Non sono miei.

Ho bisogno del seguente tempo di compilazione type_traits:

template<class T> 
struct is_derived_from_Wrap { 
    static const bool value = /* */; 
}; 

Di cosa ho bisogno?

assert(is_derived_from_Wrap<Int>::value == true); // Indeed I need static assert 
assert(is_derived_from_Wrap<MyClass>::value == true); 
assert(is_derived_from_Wrap<char>::value == false); 
struct X {}; 
assert(is_derived_from_Wrap<X>::value == false); 
+0

Ma puoi cambiare 'Int' e' MyClass'? : p – kennytm

+0

No. Grazie per il tuo suggerimento. –

+2

Non sarebbe una denominazione migliore per i tratti del tuo tipo essere: 'has_Wrap_for_base'? In realtà, MyClass non è una base di Wrap. –

risposta

9

È possibile farlo usando ma SFINAE suo genere di magia se non sai cosa sta succedendo ...

template<typename T> class Wrap { }; 

struct myclass {}; 
struct X {}; 

class Int  : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 

template< typename X > 
struct is_derived_from_Wrap 
{ 
    struct true_type { char _[1]; }; 
    struct false_type { char _[2]; }; 

    template< typename U > 
    static true_type test_sfinae(Wrap<U> * w); 
    static false_type test_sfinae(...); 

    enum { value = sizeof(test_sfinae((X*)(0)))==sizeof(true_type) }; 
}; 


#include <iostream> 
#define test(X,Y) std::cout<<(#X " == " #Y)<<" : "<<((X)?"true":"false") <<std::endl; 

int main() 
{ 
    test(is_derived_from_Wrap <Int>::value, true); 
    test(is_derived_from_Wrap <MyClass>::value, true); 
    test(is_derived_from_Wrap <char>::value, false); 
    test(is_derived_from_Wrap <X>::value, false); 
} 

Questo dà i risultati attesi

is_derived_from_Wrap <Int>::value == true : true 
is_derived_from_Wrap <MyClass>::value == true : true 
is_derived_from_Wrap <char>::value == false : false 
is_derived_from_Wrap <X>::value == false : false 

ci sono un paio di grattacapi con il mio codice. Restituirà anche true se il tipo è un Wrap.

assert( is_derived_from_Wrap< Wrap<char> >::value == 1); 

Questo può probabilmente essere risolto utilizzando un po 'più magia SFINAE se necessario.

tornerà false se la derivazione non è una derivazione pubblica (cioè è privata o protetta)

struct Evil : private Wrap<T> { }; 
assert(is_derived_from_Wrap<Evil>::value == 0); 

Ho il sospetto che questo non può essere risolto. (Ma potrei sbagliarmi). Ma sospetto che l'eredità pubblica sia sufficiente.

+0

Suppongo quindi che la semplice specializzazione parziale come nel mio codice non funzioni? – sbi

+0

Corretto in quanto la specializzazione verrà eseguita solo se il tipo corrisponde esattamente. Your is_Wrap < Wrap> :: value = true, ma is_Wrap :: value = false. –

+0

Incredibile. Ero solito pensare che 'test_sfinae (Wrap * w);' non può dedurre 'U' per il caso di' X * '. –

0

Di seguito determina se qualcosa è un involucro:

template<class T> 
struct is_Wrap { static const bool value = false; }; 

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; }; 

Dal derivazione è un Is-Un rapporto, tutto derivato da Wrap<T> è anche un Wrap<T> e deve essere trovata da questo.

+0

Il mio nome 'base di Wrap' è sbagliato. Il mio cattivo inglese. 'Derivato da wrap', ovviamente. –

+0

Non è una soluzione molto buona. Quindi ho bisogno di fare DEFINE per ogni classe. Non li conosco tutti Voglio riconoscere se X deriva da Wrap dove Y può essere di qualsiasi tipo. –

+0

@Alexey: proverò ad adattare il mio codice di conseguenza. Ma devi sicuramente essere più preciso nel porre le tue domande. (E questo non è solo un problema di barriera linguistica.) – sbi

0

È necessario eseguire una metaprogrammazione del modello abbastanza coinvolta per determinare se una classe X è derivata da un'altra Y, nel caso generale. Fondamentalmente X deriva da Y se:

  1. Y può essere convertito in modo implicito X
  2. X e Y non sono dello stesso tipo

Andrei Alexandrescu spiega come fare questo (insieme a molti altri trucchi template) nel suo libro "Modern C++ Design".

È possibile trovare il codice che risolve il problema nella libreria Loki o nell'implementazione di uSTL, entrambe scritte da Alexandrescu.

+1

Dopo aver riletto la domanda, mi rendo conto che il mio suggerimento non risolve il problema. Non vogliamo sapere se X deriva da Y, ma se X deriva da Y , dove T può essere di qualsiasi tipo. –

+0

Sì. La tua risposta non è una soluzione. Ma tu hai realizzato il problema. –

+0

Puoi anche citare boost :: is_base_of (che non affronta il problema dell'OP poiché il parametro Base non può essere un modello di classe). –