Perché std::type_info polimorfici?
-
28-09-2019 - |
Domanda
C'è un motivo per cui std::type_info
è specificato di essere polimorfici?Il distruttore è specificato essere virtuale (e non c'è un commento per l'effetto di "in modo che è polimorfico" nella Progettazione e nella Evoluzione del C++).Non riesco a vedere un motivo valido.Non ho alcun uso specifico caso, mi stavo chiedendo se mai c'è stata una spiegazione logica o la storia dietro di esso.
Ecco alcune idee che mi è venuta in mente e rifiutato:
- È un punto di estensibilità - implementazioni potrebbe definire sottoclassi, e i programmi potrebbero quindi provare a
dynamic_cast
unstd::type_info
per un altro, definito dall'implementazione del tipo derivato.Questo è probabilmente il motivo, ma sembra che è facile per le implementazioni di aggiungere un definito dall'implementazione membro, che potrebbe anche essere virtuale.I programmi che desiderano testare queste estensioni, necessariamente, essere non-portatile comunque. - È per garantire che i tipi derivati sono distrutto correttamente quando
delete
ing base puntatore.Ma non ci sono standard di tipi derivati, gli utenti non possono definire utile tipi derivati, perchétype_info
non ha standard costruttori pubblici, e cosìdelete
un ingtype_info
puntatore non è mai legale e portatile.E i tipi derivati non sono utili, perché non può essere costruito - l'unico so che non costruibili tipi derivati nell'attuazione di cose comeis_polymorphic
tipo di carattere. - Si lascia aperta la possibilità di metaclasses personalizzate tipi di ogni reale polimorfici
class A
sarebbe un "derivato metaclass"A__type_info
, che deriva datype_info
.Forse tali classi derivate potrebbe esporre i membri di chiamatanew A
con vari argomenti del costruttore in un modo type-safe, e cose del genere.Ma faretype_info
polimorfici in realtà, rende l'idea sostanzialmente impossibile da attuare, perché avrei dovuto avere metaclasses per il tuo metaclasses, all'infinito, il che è un problema se tutti itype_info
gli oggetti hanno la durata di archiviazione statica.Forse escludendo questo è il motivo per polimorfici. - C'è qualche uso per l'applicazione di RTTI caratteristiche (oltre
dynamic_cast
) perstd::type_info
di per sé, o qualcuno ha pensato che fosse carino, o imbarazzante setype_info
non era polimorfici.Ma dato che non c'è standard tipo derivato, e non le altre classi standard gerarchia che si può ragionevolmente provare croce-cast, la domanda è:cosa????C'è un uso di espressioni cometypeid(std::type_info) == typeid(typeid(A))
? - È perché gli esecutori potranno creare le proprie privato tipo derivato (come credo GCC fa).Ma perché preoccuparsi di specificarlo?Anche se il distruttore non è stato specificato come il virtuale e l'implementazione deciso che dovrebbe essere, certo che la sua attuazione potrebbe dichiarare virtuale, perché non modificare il set di operazioni consentite su
type_info
, così un programma portatile non sarebbe in grado di capire la differenza. - E ' qualcosa a che fare con compilatori parzialmente compatibile ABIs coesistenti, forse come risultato di linking dinamico.Forse gli esecutori in grado di riconoscere loro
type_info
sottoclasse (invece che uno proveniente da un altro fornitore) in modo portatile setype_info
è stato garantito virtuale.
L'ultima è la più plausibile per me al momento, ma è piuttosto debole.
Soluzione
Presumo che c'è per la comodità di realizzatori.Permette di definire estesa type_info
classi e li elimina attraverso puntatori a type_info
all'uscita dal programma, senza dover costruire speciali compilatore magia per chiamare il corretto distruttore, o altrimenti saltare attraverso i cerchi.
certo che l'attuazione dichiarare virtuale, perché non modificare il set di operazioni consentite su type_info, in modo che un programma portatile non sarebbe in grado di dire la differenza.
Non credo che sia vero.Si consideri il seguente:
#include <typeinfo>
struct A {
int x;
};
struct B {
int x;
};
int main() {
const A *a1 = dynamic_cast<const A*>(&typeid(int));
B b;
const A *a2 = dynamic_cast<const A*>(&b);
}
Se è ragionevole o no, il primo cast dinamico è consentita (e restituisce un puntatore null), mentre il secondo cast dinamico non è consentito.Quindi, se type_info
è stato definito negli standard predefinita non distruttore virtuale, ma un'implementazione aggiunto un distruttore virtuale, quindi un programma portatile potrebbe dire la differenza[*].
Sembra semplice a me di mettere il distruttore virtuale nella norma, rispetto a:
a) mettere in una nota della norma che, sebbene la definizione di classe implica che type_info
non ha funzioni virtuali, si è permesso di avere un distruttore virtuale.
b) determinare l'insieme di programmi che permette di distinguere se type_info
è polimorfica o non, e del divieto di tutti.Essi non possono essere molto utili o produttivi di programmi, non so, ma il divieto è necessario venire con un po ' di linguaggio standard che descrive l'eccezione specifica stai facendo le normali regole.
Perciò credo che lo standard sia mandato il distruttore virtuale, o con il bando.Rendendo opzionale è troppo complesso (o forse dovrei dire, penso che sarebbe giudicato inutilmente complesso.La complessità non ha mai smesso, il comitato per gli standard nelle aree in cui si è ritenuto utile...)
Se è stato bannato, però, quindi un'implementazione potrebbe:
- aggiungi un distruttore virtuale a qualche classe derivata di
type_info
- derivano tutte le sue informazioni sul tipo di oggetti da che classe
- l'uso che internamente come polimorfici classe di base per tutto
che avrebbe risolto la situazione che ho descritto all'inizio del post, ma il tipo statico di un typeid
espressione sarebbe ancora const std::type_info
, quindi sarebbe difficile per le implementazioni di definire le estensioni in cui i programmi possono dynamic_cast
vari bersagli per vedere che tipo di type_info
oggetto che hanno in un caso particolare.Forse lo standard sperava di consentire che, sebbene attuazione potrebbe offrire sempre una variante di typeid
con un diverso tipo statico, o la garanzia di un static_cast
per una certa classe di estensione funziona, e poi lasciare che il programma dynamic_cast
da lì.
In sintesi, per quanto ne so il distruttore virtuale è potenzialmente utile per gli esecutori, e che la sua rimozione non guadagnare nulla di diverso da quello che abbiamo non è possibile passare il tempo a chiedermi perché è lì ;-)
[*] In realtà, non ho dimostrato che.Ho dimostrato che un programma illegale sarebbe, a parità di tutto il resto, per la compilazione.Ma un'implementazione potrebbe forse risolvere tale da garantire che tutti non è sempre uguale, e che non si compila.Aumentare la is_polymorphic
non è portatile, così mentre è possibile per un programma di prova che una classe è polimorfici, che dovrebbe essere, non vi può essere alcun modo per un programma conforme al test di una classe non è polimorfici, che non dovrebbe essere.Penso però che anche se è impossibile, dimostrando che, al fine di rimuovere una riga dallo standard, è un bel po ' di sforzo.
Altri suggerimenti
Il C++ standard dice che typeid
restituisce un oggetto di tipo type_info, O UN DEFINITO dall'IMPLEMENTAZIONE sottoclasse di esso.Quindi...Credo che questo è più o meno la risposta.Quindi non vedo perché si rifiuta tuoi punti 1 e 2.
Paragrafo 5.2.8 Clausola 1 dell'attuale standard del C++ si legge:
Il risultato di un typeid espressione è un lvalue di tipo statico, const std::type_info (18.5.1) e dinamico tipo const std::type_info o const nome dove il nome è un definito dall'implementazione della classe derivata da std::type_info che conserva il comportamento descritto in 18.5.1.61) La vita dell'oggetto di cui al dal lvalue si estende al fine di il programma.Se o non il distruttore viene chiamato per il type_info oggetto alla fine del programma è non specificato.
Che a sua volta significa che si potrebbe scrivere il seguente codice è legale e fine:const type_info& x = typeid(expr);
che possono richiedere che type_info essere polimorfico
Circa il più semplice "globale" id che si può avere in C++ è un nome di classe,e typeinfo
fornisce un modo per confrontare tali id per l'uguaglianza.Ma il design è così scomodo e limitato che è quindi necessario avvolgere typeinfo
in qualche classe wrapper, ad es.per essere in grado di mettere le istanze raccolte.Andrei Alexandrescu fatto che nel suo "Modern C++ Design" e credo che typeinfo
wrapper è parte di Loki biblioteca;probabilmente c'è anche in Spinta;ed è abbastanza facile da arrotolare, ad es.vedere il mio wrapper.
Ma anche per un wrapper non c'è, in generale, qualsiasi necessità di un distruttore virtuale in typeinfo
.
La domanda è dunque non tanto "eh, perché c'è un distruttore virtuale", ma, per come la vedo io, "eh, perché il design così indietro, imbarazzante e non direttamente utilizzabile"?E l'ho messa giù per il processo di standardizzazione.Per esempio, iostreams non sono proprio esempi di design superbo, sia;non è qualcosa che si vuole emulare.
3/ lascia aperta la possibilità di metaclasses personalizzate tipi di ogni reale polimorfici
class A
sarebbe un "derivato metaclass"A__type_info
, che deriva datype_info
.Forse tali classi derivate potrebbe esporre i membri di chiamatanew A
con vari argomenti del costruttore in un modo type-safe, e cose del genere.Ma faretype_info
polimorfici in realtà, rende l'idea sostanzialmente impossibile da attuare, perché avrei dovuto avere metaclasses per il tuo metaclasses, all'infinito, il che è un problema se tutti itype_info
gli oggetti hanno la durata di archiviazione statica.Forse escludendo questo è il motivo per polimorfici.
Intelligente...
Comunque, sono d'accordo con questo ragionamento:tale realizzazione potrebbe facilmente rule-out meta classi per i tipi derivati da type_info
, tra cui type_info
di per sé.