Domanda

Abbiamo un sottoprogetto "commonUtils" che ha molti frammenti di codice generici utilizzati nel progetto principale.Una cosa interessante che ho visto è stata: -

/*********************************************************************
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true.  If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false
*********************************************************************/
template <class T> 
bool isPolymorphic() { 
   bool answer=false; 
   typeid(answer=true,T()); 
   return answer; 
}

Credevo il commento e pensavo che fosse un modello piuttosto interessante sebbene non fosse usato in tutto il progetto.Ho provato ad usarlo così solo per curiosità...

class PolyBase {
public:
   virtual ~PolyBase(){}
};

class NPolyBase {
public:
   ~NPolyBase(){}
};


if (isPolymorphic<PolyBase>())
  std::cout<<"PolyBase = Polymorphic\n";
if (isPolymorphic<NPolyBase>())
  std::cout<<"NPolyBase = Also Polymorphic\n";

Ma nessuno di questi restituisce mai il vero.MSVC 2005 non fornisce avvisi ma Comeau avverte che l'espressione typeid non ha alcun effetto.La sezione 5.2.8 nello standard C++ non dice nulla di simile a ciò che dice il commento, ad es.typeid viene valutato in fase di compilazione per i tipi non polimorfici e in fase di esecuzione per i tipi polimorfici.

1) Quindi immagino che il commento sia fuorviante/chiaramente sbagliato o poiché l'autore di questo codice è un programmatore C++ piuttosto esperto, mi sto perdendo qualcosa?

2) OTOH, mi chiedo se possiamo verificare se una classe è polimorfica (ha almeno una funzione virtuale) usando qualche tecnica?

3) Quando si vorrebbe sapere se una classe è polimorfica?Ipotesi selvaggia;per ottenere l'indirizzo iniziale di una classe utilizzando dynamic_cast<void*>(T) (COME dynamic_cast funziona solo su classi polimorfiche).

Aspetto i vostri pareri.

Grazie in anticipo,

È stato utile?

Soluzione

Non riesco a immaginare alcun modo possibile come questo typeid potrebbe essere utilizzato per controllare che tipo è polimorfico. Non può essere utilizzato anche per affermare che si tratta, dal momento che typeid funziona su qualsiasi tipo. Boost ha un'implementazione qui . Per quanto riguarda il motivo per cui potrebbe essere necessario - un caso che conosco è la biblioteca Boost.Serialization. Se si sta salvando tipo non polimorfica, allora si può solo salvare. Se il risparmio uno polimorfico, è necessario prende il tipo dinamico utilizzando typeid, e poi richiamare il metodo di serializzazione per quel tipo (guardando in su in qualche tabella).

Aggiorna : appare Sono davvero sbagliato. Considerate questa variante:

template <class T> 
bool isPolymorphic() { 
    bool answer=false;
    T *t = new T();
    typeid(answer=true,*t); 
    delete t;
    return answer; 
}

Questo in realtà funziona come suggerisce il nome, esattamente per commentare in snippet di codice originale. L'espressione contenuta typeid non viene valutata se "non designa un lvalue di tipo classe polimorfica" (std 3,2 / 2). Così, nel caso di cui sopra, se T non è polimorfica, l'espressione typeid non viene valutata. Se T è polimorfica, allora * t è infatti Ivalue di tipo polimorfo, così intera espressione deve essere valutato.

Ora, il tuo esempio originale è ancora :-) sbagliato. Ha usato T(), non *t. E T() creare rvalue (std 3.10 / 6). Così, produce ancora un'espressione che non è "lvalue di classe polimorfica".

Questo è abbastanza interessante trucco. D'altra parte, il suo valore pratico è piuttosto limitata - perché mentre boost :: is_polymorphic ti dà una costante fase di compilazione, questo ti dà un valore runtime, per cui non è possibile creare un'istanza codice diverso per i tipi polimorfi e non polimorfici .

Altri suggerimenti



class PolyBase {
public:   
    virtual ~PolyBase(){}
};

class NPolyBase {
public:
    ~NPolyBase(){}
};

template<class T>
struct IsPolymorphic
{
    struct Derived : T {
        virtual ~Derived();
    };
    enum  { value = sizeof(Derived)==sizeof(T) };
};


void ff()
{
    std::cout << IsPolymorphic<PolyBase >::value << std::endl;
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl;
}

Poiché C ++ 11, questo è ora disponibile nell'intestazione <type_traits> come std::is_polymorphic. Può essere utilizzato in questo modo:

struct PolyBase {
  virtual ~PolyBase() {}
};

struct NPolyBase { 
  ~NPolyBase() {}
};

if (std::is_polymorphic<PolyBase>::value)
  std::cout << "PolyBase = Polymorphic\n";
if (std::is_polymorphic<NPolyBase>::value)
  std::cout << "NPolyBase = Also Polymorphic\n";

Questo stampa solo "PolyBase = polimorfiche".

Si possono usare i fatti che:

  1. dynamic_cast fallisce in fase di compilazione se l'argomento non è una classe polimorfica.In modo che possa essere utilizzato con SFINAE.
  2. dynamic_cast<void*> è un cast valido che restituisce l'indirizzo del file completare oggetto polimorfo.

Quindi, in C++11:

#include <iostream>
#include <type_traits>

template<class T>
auto is_polymorphic2_test(T* p) -> decltype(dynamic_cast<void*>(p), std::true_type{});

template<class T>
auto is_polymorphic2_test(...) -> std::false_type;

template<class T>
using is_polymorphic2 = decltype(is_polymorphic2_test<T>(static_cast<T*>(0)));

struct A {};
struct B { virtual ~B(); };

int main() {
    std::cout << is_polymorphic2<A>::value << '\n'; // Outputs 0.
    std::cout << is_polymorphic2<B>::value << '\n'; // Outputs 1.
}

Sono un po 'confuso qui, e spero di ottenere alcuni commenti su questa risposta che spiega che cosa mi manca.

Sicuramente se si vuole scoprire se una classe è polimorfica, tutto ciò che dovete fare è chiedere se supporta dynamic_cast, non è vero?

template<class T, class> struct is_polymorphic_impl   : false_type {};
template<class T> struct is_polymorphic_impl
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {};

template<class T> struct is_polymorphic :
    is_polymorphic_impl<remove_cv_t<T>, void*> {};

Chiunque può segnalare un difetto in questa implementazione? Immagino che ci deve essere una, o deve essere stato uno ad un certo punto nel passato, perché la documentazione Boost continua a sostenere che is_polymorphic "non può essere portabile implementato nel linguaggio C ++".

Ma "portabile" è una specie di una parola donnola, giusto? Forse stanno semplicemente alludendo a come MSVC non supporta espressione-SFINAE, o alcuni dialetti come il C ++ embedded non supportano dynamic_cast. Forse quando dicono "++ linguaggio C" significano "un minimo comune denominatore sottoinsieme del linguaggio C ++." Ma ho un sospetto che forse significano quello che dicono, e Sono quello che manca qualcosa.

L'approccio typeid nel PO (come modificato da una risposta in seguito di utilizzare un non lvalue un rvalue) sembra anche bene, ma naturalmente non è constexpr e richiede in realtà la costruzione di un T, che potrebbe essere super costoso. Quindi, questo approccio dynamic_cast sembra meglio ... a meno che non funziona per qualche motivo. Pensieri?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top