2016-01-17 27 views
9

Non capisco perché il C++ permetta solo i tipi integrali e l'enum (anche enum è un tipo integrale) da definire all'interno di una dichiarazione di classe. Mentre tutti gli altri tipi, compresi i tipi a virgola mobile (vale a dire doppio e float), devono essere definiti al di fuori della dichiarazione della classe. Chiaramente, deve essere una ragione per questo, ma non riesco a capirlo.Perché solo il tipo integrale o enum può essere inizializzato in una classe C++?

Codice esempio:

#include <iostream> 

using namespace std; 

struct Node { 

    static const int c = 0; // Legal Definition 
    static const long l = 0l; // Legal Definition 
    static const short s = 0; // Legal Definition 

    static const float f = 0.0f; // Illegal definition 
    static const string S = "Test"; // Illegal definition 

    static const string JOB_TYPE; // Legal declaration 
    static const float f; // Legal declaration 
    static const double d; // Legal declaration 
}; 

const string Node::JOB_TYPE = "Test"; // correct definition 
const float Node::f = 0.0f; // correct definition 
const double Node::d = 0.0; // correct definition 

int main() { 

    cout << Node::c << endl; 
    cout << Node::c << endl; 

    cout << Node::JOB_TYPE << endl; 

    cout << Node::f << endl; 

} 
+0

Avrai solo problemi se metti la struttura 'Node' in un'intestazione e la includi in 2 file. –

+1

Questa è una domanda molto buona - Ho fatto squarciare le mie cellule cerebrali questa domenica. Grazie –

+2

Le regole peculiari per virgola mobile sono solitamente dovute alle preoccupazioni relative ai compilatori incrociati, ovvero ai compilatori che vengono eseguiti su un sistema ma che generano un codice per un altro. È abbastanza facile gestire tipi interi che corrispondono al sistema di destinazione; ottenere i dettagli dei tipi a virgola mobile di un altro sistema è molto più complicato. –

risposta

8

La ragione fondamentale è che tipi integrali (e enum perché dentro il compilatore questi diventano interi di qualche tipo) possono essere banalmente sostituiti e utilizzati direttamente come costanti.

In altre parole, struct S { static const int x = 42;}, se il compilatore vede S::x, può immediatamente sostituirlo con la costante 42 nel codice generato. Lo stesso non si applica (sempre) a float, e certamente non per tipi dipendenti dal costruttore come std::string - il compilatore non può allocare memoria per std::string senza chiamare new (o std::string::allocator). Quindi, per le costanti che devono essere "costruite" e/o che hanno criteri più complessi per il modo in cui possono essere utilizzate (pensate a un processore che non ha il supporto hardware per virgola mobile - chiamate di funzione per caricare e memorizzare valori in virgola mobile, ecc.), la lingua non può dettare che dovrebbe essere permesso di farlo.

Se si include la dichiarazione struct Node con static const std::string S = "test";, quanti posti deve contenere il compilatore Node::S? Quale dovrebbe essere usato, quando collega finalmente le tre unità di traduzione in un unico programma o dovrebbe usarne di differenti? Cosa succede se si const_castNode::S e si modifica? Quest'ultimo presuppone che tu abbia un ambiente dove questo non causa un crash, che è del tutto plausibile, e mentre quello è un comportamento indefinito, non sono sicuro che il compilatore debba renderlo così strano come usare valori diversi in ogni unità di traduzione in In quel caso ...

Modifica: Come menzionato nei commenti, C++ 11 consente di utilizzare altri tipi in modo simile, quindi le restrizioni vengono ridotte man mano che il compilatore e la tecnologia hardware vengono migliorate. Dubito che sarai mai in grado di static const std::map<X, Y> a = { ... } tho ', dato che è un tipo di dati piuttosto complesso da costruire ...

+0

"Il motivo chiave qui è che i tipi interi (e enum perché all'interno del compilatore diventano numeri interi di qualche tipo) possono essere banalmente sostituiti e usati direttamente come costanti." Più in generale, i tipi letterali possono essere usati modo - forse possiamo sperare che la regola sarà rilassata per includere questi? –

+0

Beh, il problema è che i valori letterali possono anche essere valori a virgola mobile [non sono sicuri se ce ne sono anche altri], e spesso richiedono un trattamento speciale - in particolare su hardware che non ha supporto integrato per virgola mobile. –

+1

@ChrisBeck "sarà rilassato" -> "sono rilassati". http://melpon.org/wandbox/permlink/1qxlfeqKMVWoeti7 e, più in generale, tutti i tipi letterali, [class.static.data] p3 – dyp

0

L'inizializzazione di std::string richiede che alcuni codici vengano eseguiti in fase di esecuzione.

L'inizializzazione di un puntatore con stringa letterale richiede l'inserimento della stringa da qualche parte nella memoria, anche in fase di esecuzione.