2013-06-27 17 views
6

Mam następujący kod pracy:Dlaczego nie mogę zrobić w tej klasie zainicjowany `const const std :: string` element statyczny

#include <string> 
#include <iostream> 

class A { 
public: 
    const std::string test = "42"; 
    //static const std::string test = "42"; // fails 
}; 

int main(void){ 
    A a; 
    std::cout << a.test << '\n'; 
} 

Czy istnieje dobry powód, dlaczego nie jest możliwe dokonanie test a static const? Rozumiem przed C++ 11 było ograniczone przez standard. Pomyślałem, że C++ 11 wprowadził in-class initialization, aby uczynić go trochę bardziej przyjaznym. Też nie takie semantyczne są dostępne dla typu integralnego od dłuższego czasu.

Oczywiście to działa z out-of inicjalizacji klasy w formie const std::string A::test = "42";

Myślę, że jeśli można zrobić to non-static, to problem leży w jednym z tych dwóch. Inicjowanie go poza zasięgiem klasy (zwykle const s są tworzone podczas tworzenia obiektu). Ale nie sądzę, że to jest problem, jeśli tworzysz obiekt niezależny od jakichkolwiek innych członków klasy. Drugi ma wiele definicji dla statycznego elementu. Na przykład. jeśli byłby zawarty w kilku plikach .cpp, lądując w kilku plikach obiektowych, a następnie linker miałby problemy podczas łączenia tych obiektów razem (na przykład w jeden plik wykonywalny), ponieważ zawierałyby kopie tego samego symbolu. Według mnie jest to dokładnie równe sytuacji, w której dostarcza się prawa poza klasą w deklaracji klasy w nagłówku, a następnie obejmuje ten wspólny nagłówek w więcej niż jednym miejscu. Jak pamiętam, prowadzi to do błędów linkera.

Jednak teraz odpowiedzialność za manipulację tą jest przenoszona na użytkownika/programistę. Jeśli ktoś chce mieć bibliotekę z static, musi podać definicję poza klasą, skompilować ją do oddzielnego pliku obiektowego, a następnie połączyć ze sobą wszystkie inne obiekty, mając w związku z tym tylko jedną kopię binarnej definicji symbol.

Przeczytałem odpowiedzi w Do we still need to separately define static members, even if they are initialised inside the class definition? i Why can't I initialize non-const static member or static array in class?.

nadal chcieliby wiedzieć:

  1. Czy tylko standardowe rzeczy, czy tam jest głębszy rozumowanie za tym stoi?
  2. Można to obejść przy pomocy mechanizmów literowych constexpr i zdefiniowanych przez użytkownika . Zarówno clang, jak i g ++ twierdzą, że zmienna nie może mieć typu nie-literalnego. Może uda mi się to zrobić. (Może z jakiegoś powodu jest to również zły pomysł)
  3. Czy to naprawdę duży problem, aby linker mógł umieścić tylko jeden egzemplarz symbolu: ? Ponieważ jest to static const, wszystkie powinny być niezmiennymi kopiami o dokładności binarnej .

Plese również komentarz, jeśli brakuje mi czegoś lub czegoś nie rozumiem.

+0

Jeśli chodzi o statykę, można inicjować tylko całki typu "const" i wyliczenia w miejscu deklaracji. – juanchopanza

+0

@juanchopanza Wiem o tym. Myślałem, że w C++ 11 można go pokonać. – luk32

+0

Myślę, że wskazałeś w 3. jednym z problemów. Tak, byłoby to wykonalne, ale wymagałoby to specjalnego przypadku, ponieważ w przeciwnym razie linker po prostu nie "lubi" wielu definicji. – ondrejdee

Odpowiedz

2

Twoje pytanie składa się z dwóch części. Co mówi standard? I dlaczego tak jest?

Dla elementu statycznego typu const std::string wymagane jest zdefiniowanie poza specyfikatorem klasy i posiadanie jednej definicji w jednej z jednostek tłumaczeniowych. Jest to część reguły One Definition i jest określone w klauzuli 3 standardu C++.

Ale dlaczego?

Problem polega na tym, że obiekt o statycznym czasie przechowywania potrzebuje unikalnego przechowywania statycznego w ostatecznym obrazie programu, więc musi być powiązany z jedną określoną jednostką tłumaczeniową.Specyfikator klasy nie ma domu w jednej jednostce tłumaczeniowej, po prostu definiuje typ (który musi być identycznie zdefiniowany we wszystkich jednostkach tłumaczeniowych, w których jest używany).

Powodem, dla którego stała stała nie potrzebuje pamięci, jest to, że jest ona używana przez kompilator jako wyrażenie stałe i zaznaczona w punkcie użycia. Nigdy nie dociera do obrazu programu.

Jednak typ złożony, taki jak std::string, z czasem przechowywania statycznego, wymaga przechowywania, nawet jeśli jest to const. Dzieje się tak dlatego, że mogą wymagać dynamicznego inicjowania (ich konstruktor powinien zostać wywołany przed wejściem do głównego).

Można argumentować, że kompilator powinien przechowywać informacje o obiektach o statycznym czasie przechowywania w każdej jednostce tłumaczeniowej, w której są używane, a następnie łącznik powinien łączyć te definicje w czasie łączenia w jeden obiekt w obrazie programu. Zgaduję, dlaczego tak się nie dzieje, ponieważ wymagałoby to zbyt dużej inteligencji od linkera.

+0

"Powód, dla którego integralna stała nie wymaga przechowywania, jest taki, że jest używany przez kompilator jako wyrażenie stałe i jest wstawiany w punkcie użycia, nigdy nie trafia do obrazu programu." Poza tym mogę napisać '& const_int_member', który wymaga przechowywania. I statyczna stała int, do której nigdy nie odwołuję się, nie potrzebuje przechowywania. Wreszcie, szablony wymagają dokładnie takiej samej "zbyt dużej inteligencji" od linkera. O ile mogę powiedzieć, cały ten argument jest nonsensem w każdym szczególe. (Nie, że jesteś sam w zrobieniu tego ...) – Nemo

+0

@Noo: Wykonywanie pośrednictwa ('& const_int_member') powoduje, że staje się on odr-used, a zatem wchodzi w grę wymóg zanotowany w 9.4.2p4, który oznacza, że" element członkowski nadal będzie zdefiniowany jako w zakresie przestrzeni nazw, jeśli jest używany w programie odr (3.2), a definicja zakresu przestrzeni nazw nie będzie zawierała inicjatora. " (to znaczy, że teraz wymaga jednej definicji) Jeśli chodzi o szablony, to mają specjalne jednostki inicjujące_, które używają specjalnych reguł w fazie tłumaczenia 8. –

+0

OK, ale gdy tylko zrobisz to "odr-used", ma to takie same problemy jak dowolny typ zdefiniowany przez użytkownika. Więc przesłanki wciąż się rozpadają; po prostu nie ma powodu, aby nie dopuszczać do zainicjowania jakiegokolwiek typu w nagłówku, nawet jeśli nadal potrzebna jest jedna definicja w zakresie przestrzeni nazw. To rozróżnienie między "const int" i "const whatever" naprawdę nie ma sensu, kiedy się nad tym zastanowić, o ile mogę powiedzieć ... – Nemo

Powiązane problemy