2013-03-28 16 views
9

Widziałem ten kod niedawno w pliku nagłówka i był zaskoczony, że to działało:Dlaczego dostępna jest statyczna zmienna przestrzeni nazw z wewnątrz metody klasy wbudowanej?

namespace NS { 
    static int uid = 0; 
    class X { 
    public: 
    static int getUID() { return uid++; } 
    }; 
} 

Jeśli statyczna metoda NS::X::getUID() nazywa się z kilku różnych C++ pliki źródłowe, byłem zaskoczony, że prawidłowo generowany unikalny identyfikator (unikalny dla jednostek tłumaczeniowych). Myślałem, że zmienna statyczna w zakresie przestrzeni nazw ma wewnętrzne powiązanie z jednostką tłumaczeniową. Co tu się dzieje? Czy wbudowana statyczna metoda w klasie X ma własną jednostkę tłumaczeniową i dlatego generuje unikalny identyfikator? Czy to działa dla mnie z powodu dziwactwa w moim kompilatorze?

Czy powyższy kod opiera się na "bezpiecznym" dobrze zdefiniowanym zachowaniu? Jeśli tak, jest to zaskakująco zwięzła metoda generowania unikalnego identyfikatora w klasie inline lub template, nawet jeśli wygląda trochę kludgy. A może lepiej wygenerować nowy plik źródłowy C++ dla statycznej funkcji unikalnego identyfikatora, takiej jak ta, i przenieść identyfikator statyczny wewnątrz klasy?

Aktualizacja:

Dla przypadku testowego, napisałem kilka funkcji, takich jak ten w różnych plików (file1.cpp, file2.cpp, etc.):

#include "static_def.h" // Name of the above header file. 
void func1() { 
    int uid1 = NS::X::getUID(); 
    int uid2 = NS::X::getUID(); 
    std::cout << "File1, UID1: " << uid1 << ", UID2: " << uid2 << std::endl; 
} 

Wyjście zaskakujące (po wywołaniu tych z głównego):

File1, UID1: 0, UID2: 1 
File2, UID1: 2, UID2: 3 
+0

Czy generuje unikatowe identyfikatory we wszystkich jednostkach tłumaczeniowych? Zamiast każdej jednostki tłumaczeniowej zaczynającej się od 0. –

+0

było deklaracja 'uid' w pliku nagłówkowym? Lub był "uid" zdefiniowany w pliku źródłowym? –

+1

Kto powiedział, że funkcja jest włączona? –

Odpowiedz

14

Twój kod jest poprawny tylko wtedy, gdy ten nagłówek zostanie włączony tylko w jednym pliku źródłowym.

Ponieważ uid jest zadeklarowany jako static, ma wewnętrzne powiązanie. Występuje jedna instancja zmiennej uid na plik źródłowy, do którego dołączony jest ten nagłówek. Jeśli umieścisz ten nagłówek w trzech plikach źródłowych, będą trzy zmienne: uid.

Funkcja getUid jest domyślnie inline, ponieważ jest zdefiniowana wewnątrz definicji klasy. Fakt, że jest to funkcja składowa static, jest nieistotny. Reguła dla funkcji inline polega na tym, że funkcja inline musi być zdefiniowana w każdym pliku źródłowym, w którym jest używana, oraz że wszystkie definicje muszą być identyczne.

Twój getUid definicja funkcji narusza tę zasadę: Tak, to jest zdefiniowane w każdym pliku źródłowym, który zawiera nagłówek (bo to jest zdefiniowane w nagłówku), ale każda definicja różni, ponieważ w każdej definicji uid mowa to jest inna zmienna uid.

Dlatego Twój program narusza regułę jednej definicji i wykazuje niezdefiniowane zachowanie. Obserwowane zachowanie jest prawdopodobnie spowodowane tym, że kompilator wybiera jedną definicję funkcji inline i po prostu odrzuca resztę, więc "globalna zmienna", którą uważasz za użytą, jest po prostu jedną ze zmiennych - niezależnie od tego, która z nich jest jedna. została odwołana przez kopię getUid, którą kompilator przechował. Chociaż jest to typowa manifestacja tej niezdefiniowanej formy, to jednak zachowanie jest nieokreślone.

Można zrobić uid zmienna funkcja lokalnego w celu zapewnienia, że ​​istnieje dokładnie jedna instancja, i aby uniknąć naruszania Regułę Jedna definicja:

namespace NS { 
    class X { 
    public: 
    static int getUID() { 
     static int uid = 0; 
     return uid++; 
    } 
    }; 
} 

jest gwarantowany jest w tym przypadku, że nie będzie dokładnie jeden wystąpienie uid w programie.

+0

Dobra, to właśnie podejrzewałem, że jest to niezdefiniowane zachowanie. Patrzyłem na kod produkcyjny i myślałem sobie "jak to może działać !?" Twoje wyjaśnienie ma sens i prawdopodobnie zaktualizuję kod produkcyjny z bardziej niezawodną implementacją (taką jak sugerujesz). –

+0

Ah! Nie myślałem o interakcji z ODR dla 'getUID'. Dobrze zauważony! –

2

Wierzę, że Twój kompilator cię wyśmiewa przez , nie podając metody w metodzie getUID(). Fakt, że getUID() jest domyślnie wbudowany, nie oznacza, że ​​będzie on faktycznie wstawiony - jest to jedynie zalecenie dla kompilatora.

Poza tym, wszystko w porządku: UID ma wewnętrzne powiązanie, a getUID() może równie dobrze zwracać nieunikalne identyfikatory.

Powiązane problemy