Sekcja 9.4.2, statycznymi członkami danych, od C++ standardowych stanów:
Jeżeli członek static
danych jest const
integralnej lub const
wyliczenia typu, jego oświadczenie w definicji klasy można określić const -initializator, który będzie integralnym wyrażeniem stałym.
Dlatego możliwe jest uwzględnienie wartości statycznego elementu danych "w klasie" (przez co rozumiem, że ma to znaczenie w deklaracji klasy). Jednak typ statycznego elementu danych musi być typem całkowania const
lub const
. Powodem, dla którego wartości statycznych elementów danych innych typów nie można określić w deklaracji klasy, jest to, że nietrywialna inicjalizacja jest prawdopodobnie wymagana (to znaczy, że konstruktor musi działać).
Wyobraźcie jeśli byli prawna:
// my_class.hpp
#include <string>
class my_class
{
public:
static std::string str = "static std::string";
//...
Każdy plik obiekt odpowiadający plików CPP, które zawierają ten nagłówek nie tylko kopię miejsca dla my_class::str
(obejmującej sizeof(std::string)
bajtów), ale również "sekcję ctor", która wywołuje konstruktor std::string
, pobierając ciąg znaków C. Każda kopia przestrzeni do przechowywania dla my_class::str
byłaby identyfikowana przez wspólną etykietę, więc łącznik teoretycznie może połączyć wszystkie kopie przestrzeni pamięci w pojedynczą. Jednak linker nie będzie w stanie wyizolować wszystkich kopii kodu konstruktora w sekcjach ctor plików obiektów. To tak, jakby prosząc łącznik, aby usunąć cały kod do zainicjowania str
w kompilacji z następujących powodów:
std::map<std::string, std::string> map;
std::vector<int> vec;
std::string str = "test";
int c = 99;
my_class mc;
std::string str2 = "test2";
EDIT Pouczające jest patrzeć na wyjściu montera g ++ za pomocą następującego kodu:
// SO4547660.cpp
#include <string>
class my_class
{
public:
static std::string str;
};
std::string my_class::str = "static std::string";
Kod zespół może być uzyskane przez wykonanie:
g++ -S SO4547660.cpp
patrząc przez SO4547660.s
plik generowany przez g ++, można zauważyć, że istnieje dużo kodu dla tak małego pliku źródłowego.
__ZN8my_class3strE
to etykieta miejsca do przechowywania my_class::str
.Istnieje również źródło zespołu funkcji __static_initialization_and_destruction_0(int, int)
, która ma etykietę __Z41__static_initialization_and_destruction_0ii
. Ta funkcja jest wyjątkowa dla g ++, ale wystarczy wiedzieć, że g ++ upewni się, że zostanie wywołana przed wykonaniem jakiegokolwiek kodu nieinicjalizującego. Zauważ, że implementacja tej funkcji wywołuje __ZNSsC1EPKcRKSaIcE
. To jest zniekształcony symbol dla std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
.
Wracając do hipotetycznym przykładzie powyżej i stosując te szczegóły, każdy plik obiekt odpowiadający pliku CPP, który zawiera my_class.hpp
miałby etykietę __ZN8my_class3strE
dla sizeof(std::string)
bajtów, a także kod montaż zadzwonić __ZNSsC1EPKcRKSaIcE
w jego realizacji __static_initialization_and_destruction_0(int, int)
funkcjonować. Łącznik może z łatwością łączyć wszystkie wystąpienia __ZN8my_class3strE
, ale nie może wyizolować kodu, który wywołuje __ZNSsC1EPKcRKSaIcE
w implementacji pliku obiektu __static_initialization_and_destruction_0(int, int)
.
Reguła jednej definicji to: "Żadna jednostka tłumaczeniowa nie może zawierać więcej niż jednej definicji żadnej zmiennej, funkcji, typu klasy, typu wyliczeniowego lub szablonu". Jeśli Twój pierwszy przykład "Gizmo" byłby legalny, nie sądzę, że naruszałby zasadę One Definition, ponieważ każda jednostka tłumaczeniowa * miałaby * jedną definicję "Gizmo :: name". –
@ Daniel Trebbien: To nie jest cała ODR. To tylko 3,2/1 - pierwsza szorstka warstwa "warstwy" ODR (aby zadbać o najbardziej oczywiste naruszenia). Pełna ODR zawiera bardziej szczegółowy zestaw wymagań dla każdego rodzaju jednostki. W przypadku obiektów z zewnętrznym łączem (jak również z funkcjami połączeń zewnętrznych), ODR jest dodatkowo ograniczony w 3.2/3 do jednej i jedynej definicji * dla całego programu *. – AnT
@ Daniel Trebbien: Powodem, dla którego wymóg 3.2/1 został oddzielony od reszty, jest to, że naruszenie 3.2/1 wymaga diagnostyki z kompilatora, podczas gdy w przypadku naruszenia 3.2/3 diagnostyka nie jest wymagana. – AnT