2016-01-17 30 views
9

Nie rozumiem, dlaczego C++ dopuszcza tylko typy całkowite i wyliczenia (wyliczenie jest również typu integralnego), które zostaną zdefiniowane w deklaracji klasy. Wszystkie pozostałe typy, w tym typy punktów ruchomych (tj. Podwójne i zmiennoprzecinkowe), muszą być zdefiniowane poza deklaracją klasową. Najwyraźniej musi to być powód, ale nie mogę tego rozgryźć.Dlaczego tylko typ całkowy lub wyliczeniowy można zainicjować w klasie C++?

przykładem Kod:

#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

Będziesz miał problem, jeśli wstawisz strukturę 'Node' w nagłówku i umieścisz ją w 2 plikach. –

+1

To jest bardzo dobre pytanie - Mam komórki mózgowe, które ratują tę niedzielę. Dzięki –

+2

Peculiarne reguły dla zmiennoprzecinkowych są zwykle spowodowane obawami dotyczącymi kompilatorów krzyżowych, czyli kompilatorów działających na jednym systemie, ale generujących kod dla innego. Łatwo radzić sobie z typami integralnymi, które pasują do systemu docelowego; uzyskanie szczegółów na temat typów zmiennoprzecinkowych innego systemu jest znacznie bardziej skomplikowane. –

Odpowiedz

8

Głównym powodem jest to, że integralne typy (i enum ponieważ wewnątrz kompilator te stają się całkowitymi z jakiegoś) może być zastąpiony trywialnie i wykorzystywane bezpośrednio jako stałe.

Innymi słowy, struct S { static const int x = 42;}, jeśli kompilator widzi S::x, może natychmiast zamienić go na stałą 42 w wygenerowanym kodzie. To samo nie ma (zawsze) zastosowania do float, a na pewno nie dla typów zależnych od konstruktorów, takich jak std::string - kompilator nie może przydzielić pamięci dla std::string bez wywoływania new (lub std::string::allocator). Tak więc, dla stałych, które muszą być "skonstruowane" i/lub mają bardziej złożone kryteria, jak mogą być użyte (myślę o procesorze, który nie ma wsparcia sprzętowego dla zmiennoprzecinkowych - wywołania funkcji do ładowania i przechowywania wartości zmiennoprzecinkowych, itp.), język nie może dyktować, że powinno to być dozwolone.

Jeśli dołączysz deklarację struct Node z static const std::string S = "test";, to ile miejsc ma przechowywać sklep kompilatora Node::S? Którego z nich należy użyć, kiedy ostatecznie łączy twoje trzy jednostki tłumaczeniowe w jeden program - czy też powinien używać innych? Co się stanie, jeśli const_cast z Node::S i go zmodyfikować? Ten ostatni zakłada, że ​​masz środowisko, w którym to nie powoduje awarii, co jest całkowicie wiarygodne, i chociaż jest to niezdefiniowane zachowanie, nie jestem pewien, czy kompilator powinien sprawić, że będzie tak dziwnie, jak używanie różnych wartości w każdej jednostce tłumaczeniowej w w tym przypadku ...

Edycja: Jak wspomniano w komentarzach, C++ 11 pozwala na używanie kolejnych typów w podobny sposób, więc ograniczenia są rozluźniane, gdy kompilator i technologia sprzętowa jest ulepszana. Wątpię, czy kiedykolwiek będziesz w stanie "" stworzyć skomplikowany typ danych ...

+0

"Kluczowy powód jest taki, że typy integralne (i wyliczeniowe, ponieważ wewnątrz kompilatora stają się takimi rodzajami całkowitymi) mogą być trywialnie zastąpione i użyte bezpośrednio jako stałe." Bardziej ogólnie, można stosować typy dosłowne sposób - może możemy mieć nadzieję, że reguła zostanie złagodzona, aby je uwzględnić? –

+0

Problem polega na tym, że literały mogą być wartościami zmiennoprzecinkowymi [nie wiem, czy są też inne] i często wymagają specjalnego traktowania - szczególnie w przypadku sprzętu, który nie ma wbudowanej obsługi punktu zmiennoprzecinkowego. –

+1

@ChrisBeck "będzie zrelaksowany" -> "jest zrelaksowany". http://melpon.org/wandbox/permlink/1qxlfeqKMVWoeti7 i bardziej ogólnie, wszystkie typy liter, [class.static.data] p3 – dyp

0

Inicjalizacja std::string wymaga wykonania kodu w czasie wykonywania.

Inicjowanie wskaźnika za pomocą literału ciągłego wymaga umieszczenia łańcucha w pamięci, także w czasie wykonywania.