Nienazwane / anonimowe przestrzenie nazw a funkcje statyczne

Cechą C++ jest możliwość tworzenia nienazwanych (anonimowych) przestrzeni nazw, jak tak:

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

Można by pomyśleć, że taka funkcja byłaby bezużyteczna-ponieważ nie można podać nazwy przestrzeni nazw, nie można uzyskać dostępu do czegokolwiek wewnątrz niej z zewnątrz. Ale te nienazwane przestrzenie nazw dostępne w pliku, w którym zostały utworzone, tak jakbyś miał do nich ukrytą klauzulę using-clause.

Moje pytanie brzmi, dlaczego i kiedy byłoby to korzystne dla korzystanie z funkcji statycznych? Czy są one zasadniczo dwa sposoby robienia dokładnie tego samego?

Author: Raedwald, 2008-09-30

11 answers

Standard C++ odczytuje w sekcji 7.3.1.1 nienazwane przestrzenie nazw, paragraf 2:

Użycie słowa kluczowego static jest przestarzałe przy deklarowaniu obiektów w namespace scope, the unnamed-namespace zapewnia doskonałą alternatywę.

Statyczne dotyczą tylko nazw obiektów, funkcji i anonimowych związków, a nie deklaracji typu.

Edit:

Decyzja o wycofaniu użycia słowa kluczowego static (wpływającego na widoczność deklaracja zmiennej w jednostce tłumaczenia) została odwrócona (ref ). W tym przypadku używanie static lub bezimiennego {[2] } jest zasadniczo dwa sposoby robienia dokładnie tego samego. Aby uzyskać więcej dyskusji zobacz this SO question.

Unnamed namespace ' S nadal ma tę zaletę, że pozwala zdefiniować typy translation-unit-local. Proszę zobaczyć to więc pytanie o więcej szczegółów.

Podziękowania należą się Mike 'owi Percy' emu za doprowadzenie tego do mojego Uwaga.

 354
Author: luke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-12 01:40:36

Umieszczenie metod w anonimowej przestrzeni nazw zapobiega przypadkowemu naruszeniujednej reguły definicji , pozwalając nigdy nie martwić się o nazywanie metod pomocniczych tak samo, jak innych metod, do których można się połączyć.

I, jak zauważył luke, anonimowe przestrzenie nazw są preferowane przez standard niż statyczne elementy.

 76
Author: hazzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 23:56:57

Jest jeden przypadek edge, gdzie statyka ma zaskakujący efekt(przynajmniej dla mnie). Standard C++03 stanowi w 14.6.4.2 / 1:

Dla wywołania funkcji, które zależy od parametru szablonu, jeśli nazwa funkcji jest unqualified-id, ale nie template-id , kandydujące funkcje znajdują się przy użyciu zwykłych reguł wyszukiwania (3.4.1, 3.4.2) z wyjątkiem:

  • dla części wyszukiwania używającej funkcji unqualified name lookup (3.4.1), tylko funkcja znajdują się deklaracje z linkami zewnętrznymi z kontekstu definicji szablonu.
  • dla części wyszukiwania wykorzystującej powiązane przestrzenie nazw (3.4.2), znaleziono tylko deklaracje funkcji z powiązaniem zewnętrznym w kontekście definicji szablonu lub kontekstu instancji szablonu.

...

Poniższy kod wywoła foo(void*), a nie foo(S const &), Jak można się spodziewać.

template <typename T>
int b1 (T const & t)
{
  foo(t);
}

namespace NS
{
  namespace
  {
    struct S
    {
    public:
      operator void * () const;
    };

    void foo (void*);
    static void foo (S const &);   // Not considered 14.6.4.2(b1)
  }

}

void b2()
{
  NS::S s;
  b1 (s);
}
Sama w sobie nie jest to chyba aż tak wielka sprawa, ale ma zwróć uwagę, że dla w pełni zgodnego kompilatora C++ (tj. takiego z obsługą export) słowo kluczowe static nadal będzie miało funkcjonalność, która nie jest dostępna w żaden inny sposób.
// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
  foo(t);
}

// foo.cc
#include "bar.h"
namespace NS
{
  namespace
  {
    struct S
    {
    };

    void foo (S const & s);  // Will be found by different TU 'bar.cc'
  }
}

void b2()
{
  NS::S s;
  b1 (s);
}

Jedynym sposobem, aby upewnić się, że funkcja w naszej nienazwanej przestrzeni nazw nie zostanie znaleziona w szablonach używających ADL, jest jej utworzenie static.

Update for Modern C++

W C++ ' 11, członkowie bezimiennej przestrzeni nazw mają wewnętrzne powiązania w sposób niejawny (3.5/4):

An unnamed przestrzeń nazw lub Przestrzeń nazw zadeklarowana bezpośrednio lub pośrednio w nienazwanej przestrzeni nazw ma wewnętrzne powiązanie.

Ale w tym samym czasie, 14.6.4.2/1 został zaktualizowany, aby usunąć wzmiankę o linkage (to zaczerpnięte z C++ '14):

Dla wywołania funkcji, gdzie wyrażenie postfix jest nazwą zależną, funkcje kandydujące znajdują się za pomocą zwykłe reguły wyszukiwania (3.4.1, 3.4.2) z tym wyjątkiem, że:

  • Dla części wyszukiwania przy użyciu funkcji wyszukiwania nazw niekwalifikowanych (3.4.1), znajdują się tylko deklaracje funkcji z kontekstu definicji szablonu.

  • W przypadku części wyszukiwania wykorzystującej powiązane przestrzenie nazw (3.4.2) można znaleźć tylko deklaracje funkcji Znalezione w kontekście definicji szablonu lub kontekstu instancji szablonu.

W rezultacie ta szczególna różnica między statycznymi i nienazwanymi członkami przestrzeni nazw już nie istnieje.

 38
Author: Richard Corden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-26 10:33:27

Ostatnio zacząłem zastępować statyczne słowa kluczowe anonimowymi przestrzeniami nazw w moim kodzie, ale od razu napotkałem problem, w którym zmienne w przestrzeni nazw nie były już dostępne do wglądu w moim debuggerze. Używałem VC60, więc nie wiem, czy to nie jest problem z innymi debuggerami. Moim obejściem było zdefiniowanie przestrzeni nazw "modułu", w której nadałem mu nazwę mojego pliku cpp.

Na przykład w moim XmlUtil.plik cpp, definiuję przestrzeń nazw XmlUtil_I { ... } dla wszystkich moich zmiennych modułu i funkcje. W ten sposób mogę zastosować kwalifikację XmlUtil_I:: w debuggerze, aby uzyskać dostęp do zmiennych. W tym przypadku _I odróżnia ją od publicznej przestrzeni nazw, takiej jak XmlUtil, której mogę chcieć użyć gdzie indziej.

Przypuszczam, że potencjalną wadą tego podejścia w porównaniu z prawdziwie anonimowym jest to, że ktoś może naruszyć żądany zakres statyczny, używając kwalifikatora przestrzeni nazw w innych modułach. Nie wiem, czy to poważny problem.

 12
Author: Evg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-06 09:04:43

Użycie statycznego słowa kluczowego w tym celu jest przestarzałe przez standard C++98. Problem z statyką polega na tym, że nie ma ona zastosowania do definicji typu. Jest to również przeciążone słowo kluczowe używane na różne sposoby w różnych kontekstach, więc nienazwane przestrzenie nazw nieco upraszczają.

 7
Author: Firas Assaad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:13:35

Z doświadczenia po prostu zauważę, że chociaż jest to sposób c++ na umieszczenie wcześniej statycznych funkcji w anonimowej przestrzeni nazw, starsze Kompilatory mogą czasami mieć z tym problemy. Obecnie pracuję z kilkoma kompilatorami dla naszych docelowych platform, a bardziej nowoczesny kompilator Linuksa jest w porządku z umieszczaniem funkcji w anonimowej przestrzeni nazw.

Ale starszy kompilator działający na Solarisie, z którym jesteśmy związani do czasu nieokreślonego przyszłego wydania, czasami go zaakceptuje, a innym razem zaznacz to jako błąd. Błąd nie jest tym, co mnie martwi, ale tym, co Może robić, gdy zaakceptuje. Więc dopóki nie przejdziemy do nowoczesności, nadal Używamy statycznych (Zwykle klasowych) funkcji, w których preferujemy anonimową przestrzeń nazw.

 6
Author: Don Wakefield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-26 20:43:26

Osobiście wolę funkcje statyczne niż bezimienne przestrzenie nazw z następujących powodów:

  • Jest oczywiste i jasne z samej definicji funkcji, że jest prywatna do jednostki tłumaczeniowej, w której jest kompilowana. W przypadku bezimiennej przestrzeni nazw może być konieczne przewijanie i wyszukiwanie, aby sprawdzić, czy funkcja znajduje się w przestrzeni nazw.

  • Funkcje w przestrzeniach nazw mogą być traktowane jako extern przez niektóre (starsze) Kompilatory. W VS2017 są one nadal extern. Z tego powodu nawet jeśli funkcja jest w bezimiennej przestrzeni nazw możesz nadal chcieć oznaczyć je statycznie.

  • Funkcje statyczne zachowują się bardzo podobnie w C lub C++, podczas gdy bezimienne przestrzenie nazw są oczywiście tylko C++. bezimienne przestrzenie nazw również dodają dodatkowy poziom w wcięciach i nie podoba mi się to :)

Więc cieszę się, że używanie statycznych funkcji nie jest już przestarzałe.

 6
Author: Pavel P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-08 08:30:36

Dodatkowo jeśli na zmiennej używa się statycznego słowa kluczowego:

namespace {
   static int flag;
}

Nie będzie widoczny w pliku mapowania

 3
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-10 20:27:10

Różnica polega na nazwie zniekształconego identyfikatora (_ZN12_GLOBAL__N_11bE vs _ZL1b, co tak naprawdę nie ma znaczenia, ale oba są połączone z lokalnymi symbolami w tabeli symboli (brak .global dyrektywy asm).

#include<iostream>
namespace {
   int a = 3;
}

static int b = 4;
int c = 5;

int main (){
    std::cout << a << b << c;
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_11aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4
_ZL1b:
        .long   4
        .globl  c
        .align 4
        .type   c, @object
        .size   c, 4
c:
        .long   5
        .text

Jak dla zagnieżdżonej anonimowej przestrzeni nazw:

namespace {
   namespace {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_112_GLOBAL__N_11aE:
        .long   3

Wszystkie anonimowe przestrzenie nazw pierwszego poziomu w jednostce tłumaczenia są ze sobą połączone, wszystkie zagnieżdżone anonimowe przestrzenie nazw drugiego poziomu w jednostce tłumaczenia są połączone ze sobą

Możesz też mieć zagnieżdżona przestrzeń nazw lub zagnieżdżona wewnętrzna przestrzeń nazw w anonimowej przestrzeni nazw]}

namespace {
   namespace A {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11A1aE, @object
        .size   _ZN12_GLOBAL__N_11A1aE, 4
_ZN12_GLOBAL__N_11A1aE:
        .long   3

which for the record demangles as:
        .data
        .align 4
        .type   (anonymous namespace)::A::a, @object
        .size   (anonymous namespace)::A::a, 4
(anonymous namespace)::A::a:
        .long   3

//inline has the same output

Możesz również mieć anonimowe przestrzenie nazw, ale z tego, co wiem, inline w anonimowej przestrzeni nazw ma efekt 0

inline namespace {
   inline namespace {
       int a = 3;
    }
}

_ZL1b: _Z oznacza, że jest to zniekształcony identyfikator. L oznacza, że jest to symbol lokalny przez static. 1 jest długością identyfikatora b, a następnie identyfikatora b

_ZN12_GLOBAL__N_11aE _Z oznacza, że jest to zniekształcony identyfikator. N oznacza, że jest to przestrzeń nazw 12 jest długością anonimowej przestrzeni nazw _GLOBAL__N_1, następnie anonimowej przestrzeni nazw _GLOBAL__N_1, następnie {[12] } jest długością identyfikatora a, a jest identyfikatorem a i E zamyka identyfikator, który znajduje się w przestrzeni nazw.

_ZN12_GLOBAL__N_11A1aE jest taka sama jak powyżej, z tym, że jest w niej inny poziom przestrzeni nazw 1A

 3
Author: Lewis Kelsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-20 00:57:42

Specyficzna dla kompilatora różnica między anonimowymi przestrzeniami nazw a statycznymi funkcjami może być widoczna podczas kompilacji poniższego kodu.

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

Kompilacja tego kodu Z VS 2017 (określenie flagi ostrzegawczej poziomu 4 /W4, aby włączyć Ostrzeżenie C4505: unreferenced local function has been removed) i gcc 4.9 z flagą-wunused-function lub-Wall pokazuje, że VS 2017 wyświetli tylko ostrzeżenie dla nieużywanej funkcji statycznej. gcc 4.9 i wyższe, a także clang 3.3 i wyższe, będzie generuje ostrzeżenia dla funkcji niezrealizowanej w przestrzeni nazw, a także ostrzeżenie dla nieużywanej funkcji statycznej.

Demo gcc 4.9 i MSVC 2017

 2
Author: masrtis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-18 06:16:55

Dowiedziawszy się o tej funkcji dopiero teraz, czytając twoje pytanie, mogę tylko spekulować. Wydaje się, że daje to kilka zalet w stosunku do zmiennej statycznej na poziomie pliku:

  • anonimowe przestrzenie nazw mogą być zagnieżdżane między sobą, zapewniając wiele poziomów ochrony, z których symbole nie mogą uciec.
  • kilka anonimowych przestrzeni nazw może być umieszczonych w tym samym pliku źródłowym, tworząc w efekcie różne zakresy na poziomie statycznym w tym samym pliku.

I ' d bądź zainteresowany nauką, jeśli ktoś używał anonimowych przestrzeni nazw w prawdziwym kodzie.

 0
Author: Commodore Jaeger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:09:26