C ++: type_info per distinguere i tipi
Domanda
So che i compilatori hanno molta libertà nella realizzazione comportamento funzioni std::type_info
.
Sto pensando di usare per confrontare i tipi di oggetto, quindi mi piacerebbe essere sicuri che:
-
std::type_info::name
deve restituire due stringhe differenti per due tipi diversi. -
std::type_info::before
devo dire cheType1
è primaType2
esclusivo oType2
è primaType1
.// like this: typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
-
Due diversa specializzazione della stessa classe template sono considerati tipi diversi.
-
Due differenti
typedef
-initions dello stesso tipo sono dello stesso tipo.
E infine:
-
Dato
std::type_info
non è copiabile, come avrei potuto conservaretype_info
s da qualche parte (ad esempio: in unstd::map
)? L'unico modo per avere unstd::type_info
da qualche parte sempre assegnato (ad esempio: sullo stack o su una variabile statica / globale)? E utilizzare un puntatore ad esso -
Quanto velocemente sono
operator==
,operator!=
ebefore
sulla maggior parte dei compilatori comuni? Credo che dovrebbero confrontare solo un valore. E quanto velocemente ètypeid
? -
Ho una
A
classe con unvirtual bool operator==( const A& ) const
. DalA
ha molte sottoclassi (alcuni dei quali sono sconosciuti al momento della compilazione), mi piacerebbe overload che operatore virtuale in qualsiasiB
sottoclasse in questo modo:virtual bool operator==( const A &other ) const { if( typeid(*this) != typeid(other) ) return false; // bool B::operator==( const B &other ) const // is defined for any class B return operator==( static_cast<B&>( other ) ); }
E 'questo un modo accettabile (e standard) per implementare tale operatore?
Soluzione
Dopo una rapida un'occhiata alla documentazione, direi che:
-
std :: :: type_info name restituisce sempre due stringhe differenti per due tipi diversi, altrimenti vuol dire che il compilatore si perdeva durante la risoluzione tipi e non si deve usare più.
-
Riferimento dice: "prima del ritorno TRUE se il tipo precede il tipo di RHS nell'ordine collazione L'ordine di collazione è solo un ordine interno tenuto da una particolare implementazione e non è necessariamente correlate a rapporti di successione o ordine dichiarando. ". È quindi avere la garanzia che nessun tipo ha lo stesso rango nell'ordine collazione.
-
Ogni istanza di una classe template è un diverso tipo. La specializzazione non fare eccezioni.
-
Io non capisco quello che vuoi dire. Se qualcosa di media come avere
typedef foo bar;
in due unità distinte di compilazione e quel bar è lo stesso in entrambi, funziona in questo modo. Se vuoi diretypedef foo bar; typedef int bar;
, non funziona (a meno che foo è int).
A proposito vostre altre domande:
- Si dovrebbe memorizzare i riferimenti a std :: type_info, di avvolgere in qualche modo.
- assolutamente idea circa le prestazioni, suppongo che gli operatori di confronto hanno tempo costante nonostante il tipo di complessità. Prima deve avere complessità lineare in funzione del numero di tipi diversi utilizzati nel codice.
- Questo è davvero strano imho. Si dovrebbe sovraccaricare il
operator==
invece di Make IT virtuale e sostituirla.
Altri suggerimenti
standard 18.5.1 (Classe type_info):
Il type_info classe descrive tipo informazioni generate dal implementazione. Oggetti di questa classe memorizzare in modo efficace un puntatore ad un nome per il tipo, e di un valore codificato atto a confrontare due tipi di l'uguaglianza o l'ordine di collazione. Il nomi, regola la codifica, e fascicolazione sequenza per i tipi sono tutti non specificato e possono differire tra i programmi .
Dalla mia comprensione:
- Non hai questa garanzia per quanto riguarda
std:type_info::name
. La norma afferma soltanto che restituiscename
un NTB implementazione definita ??em>, e credo che un conforme attuazione potrebbe benissimo tornare la stessa stringa per ogni tipo. - Non lo so, e lo standard non è chiaro su questo punto, in modo da non fare affidamento su tale comportamento.
- che uno dovrebbe essere un preciso 'Sì' per me
- che uno dovrebbe essere un preciso 'Sì' per me
Per quanto riguarda la seconda serie di domande:
- No, non è possibile memorizzare un
type_info
. Andrei Alexandrescu propone un involucroTypeInfo
nella sua libro moderno C ++ design . Nota che gli oggetti restituiti datypeid
hanno stoccaggio statico in modo da poter memorizzare in modo sicuro i puntatori, senza preoccuparsi di durata degli oggetti - Credo che si può supporre che il confronto
type_info
sono estremamente efficienti (non c'è davvero molto da confrontare).
È possibile memorizzare in questo modo.
class my_type_info
{
public:
my_type_info(const std::type_info& info) : info_(&info){}
std::type_info get() const { return *info_;}
private:
const std::type_info* info_;
};
EDIT:
C ++ standard di 5.2.8.
Il risultato di un espressione typeid è un Ivalue di tipo statico const std :: type_info ...
Il che significa che è possibile utilizzarlo in questo modo.
my_type_info(typeid(my_type));
La funzione typeid restituisce un lvalue (non è temporanea) e quindi l'indirizzo del restituita type_info è sempre valido.
Le risposte attuali per le domande 1 e 2 sono perfettamente corrette, e sono essenzialmente solo dettagli per la classe type_info -. Inutile ripetere quelle risposte
Per domande 3 e 4, è importante capire che cosa è esattamente un tipo in C ++, e come si riferiscono ai nomi. Per cominciare, ci sono un sacco di tipi predefiniti, e quelli hanno nomi: int, float, double
. Poi, ci sono alcuni tipi costruiti che fare non hanno nomi dei loro propri: const int, int*, const int*, int* const
. Ci sono tipi di funzione int (int)
e puntatore a funzione tipi int (*)(int)
.
A volte è utile per dare un nome ad un tipo senza nome, che è possibile utilizzare typedef
. Ad esempio, typedef int* pint
o typedef int (*pf)(int);
. Questo introduce un nome, non un nuovo tipo.
Avanti sono definiti dall'utente tipi: le strutture, le classi, i sindacati. C'è una buona convenzione per dare loro i nomi, ma non è obbligatorio. Non aggiungere tale nome una con typedef, è possibile farlo direttamente: struct Foo { };
invece di typedef struct {} Foo;
. E 'comune per avere definizioni di classe nelle intestazioni, che finiscono \ in più unità di traduzione. Ciò significa che la classe è definita più di una volta. Questo è ancora dello stesso tipo, e quindi non sono autorizzati a giocare trucchi con le macro per modificare le definizioni di membro di classe.
Una classe template è non un tipo, è una ricetta per i tipi. Due istanze di un singolo modello di classe sono tipi distinti se gli argomenti template sono diversi tipi (o valori). Questo funziona in modo ricorsivo:. Dato template <typename T> struct Foo{};
, Foo<Foo<int> >
è dello stesso tipo di Foo<Foo<Bar> >
se e solo se Bar
è un altro nome per il tipo int
type_info è implementazione definita in modo davvero non fare affidamento su di esso. Tuttavia, in base alle mie esperienze con g ++ e MSVC, le ipotesi 1,3 e 4 stretta ... non proprio sicuro di # 2.
C'è qualche motivo non è possibile utilizzare un altro metodo come questo?
template<typename T, typename U>
struct is_same { static bool const result = false; };
template<typename T>
struct is_same<T, T> { static bool const result = true; };
template<typename S, typename T>
bool IsSame(const S& s, const T& t) { return is_same<S,T>::result; }