Obory názvů (C++)

Obor názvů je deklarativní oblast, která poskytuje obor identifikátorů (názvy typů, funkcí, proměnných atd.). Obory názvů slouží k uspořádání kódu do logických skupin a k prevenci kolizí názvů, ke kterým může dojít zejména v případě, že základ kódu obsahuje více knihoven. Všechny identifikátory v oboru názvů jsou viditelné pro sebe bez kvalifikace. Identifikátory mimo obor názvů mají přístup ke členům pomocí plně kvalifikovaného názvu každého identifikátoru, například pomocí deklarace pro jeden identifikátor (using std::string) nebo direktivy using pro všechny identifikátory v oboru názvů (using namespace std;).std::vector<std::string> vec; Kód v souborech hlaviček by měl vždy používat plně kvalifikovaný název oboru názvů.

Následující příklad ukazuje deklaraci oboru názvů a tři způsoby, jak kód mimo obor názvů může přistupovat ke svým členům.

namespace ContosoData
{
    class ObjectManager
    {
    public:
        void DoSomething() {}
    };
    void Func(ObjectManager) {}
}

Použijte plně kvalifikovaný název:

ContosoData::ObjectManager mgr;
mgr.DoSomething();
ContosoData::Func(mgr);

K přenesení jednoho identifikátoru do oboru použijte deklaraci using:

using ContosoData::ObjectManager;
ObjectManager mgr;
mgr.DoSomething();

Použití direktivy using k přenesení všeho v oboru názvů do oboru:

using namespace ContosoData;

ObjectManager mgr;
mgr.DoSomething();
Func(mgr);

using – direktivy

Direktiva using umožňuje použití všech názvů v oboru namespace názvů bez názvu oboru názvů jako explicitního kvalifikátoru. Použití direktivy using v implementačním souboru (tj. *.cpp), pokud v oboru názvů používáte několik různých identifikátorů; pokud používáte jenom jeden nebo dva identifikátory, zvažte použití deklarace, která tyto identifikátory přenese pouze do oboru názvů, a ne všechny identifikátory v oboru názvů. Pokud má lokální proměnná stejný název jako proměnná oboru názvů, je proměnná oboru názvů skryta. Jedná se o chybu, pokud má proměnná oboru názvů stejný název jako globální proměnná.

Poznámka:

Direktivu using lze umístit na začátek souboru .cpp (v oboru souboru) nebo uvnitř definice třídy nebo funkce.

Obecně platí, že nepoužívejte direktivy using do souborů hlaviček (*.h), protože jakýkoli soubor, který obsahuje tuto hlavičku, přenese všechno v oboru názvů do oboru názvů, což může způsobit problémy s skrytím názvů a kolizí názvů, které jsou velmi obtížné ladit. Vždy používejte plně kvalifikované názvy v souboru hlaviček. Pokud jsou tyto názvy příliš dlouhé, můžete je zkrátit pomocí aliasu oboru názvů. (Viz níže.)

Deklarace oborů názvů a členů oboru názvů

Obvykle deklarujete obor názvů v souboru hlaviček. Pokud jsou implementace funkcí v samostatném souboru, opravněte názvy funkcí, jako v tomto příkladu.

// contosoData.h
#pragma once
namespace ContosoDataServer
{
    void Foo();
    int Bar();
}

Implementace funkcí v souboru contosodata.cpp by měly používat plně kvalifikovaný název, i když umístíte using direktivu na začátek souboru:

#include "contosodata.h"
using namespace ContosoDataServer;

void ContosoDataServer::Foo() // use fully-qualified name here
{
   // no qualification needed for Bar()
   Bar();
}

int ContosoDataServer::Bar(){return 0;}

Obor názvů lze deklarovat v několika blocích v jednom souboru a v několika souborech. Kompilátor spojí části během předběžného zpracování a výsledný obor názvů obsahuje všechny členy deklarované ve všech částech. Příkladem je obor názvů std, který je deklarován v každém ze souborů hlaviček ve standardní knihovně.

Členy pojmenovaného oboru názvů mohou být definovány mimo obor názvů, ve kterém jsou deklarovány explicitní kvalifikace názvu, který je definován. Definice se však musí objevit za bodem deklarace v oboru názvů, který uzavře obor názvů deklarace. Příklad:

// defining_namespace_members.cpp
// C2039 expected
namespace V {
    void f();
}

void V::f() { }        // ok
void V::g() { }        // C2039, g() is not yet a member of V

namespace V {
    void g();
}

K této chybě může dojít, když jsou členy oboru názvů deklarovány napříč více soubory hlaviček a tyto hlavičky jste nezahrnuli do správného pořadí.

Globální obor názvů

Pokud identifikátor není deklarován v explicitním oboru názvů, je součástí implicitního globálního oboru názvů. Obecně se snažte vyhnout vytváření deklarací v globálním oboru, pokud je to možné, s výjimkou hlavní funkce vstupního bodu, která musí být v globálním oboru názvů. Pokud chcete explicitně kvalifikovat globální identifikátor, použijte operátor překladu oboru bez názvu, jako v ::SomeFunction(x);. Tím se identifikátor odliší od čehokoli se stejným názvem v jakémkoli jiném oboru názvů a pomůže vám také usnadnit pochopení kódu ostatním.

Obor názvů std

Všechny typy a funkce standardní knihovny jazyka C++ jsou deklarovány v std oboru názvů nebo oborech názvů vnořených uvnitř std.

Vnořené obory názvů

Obory názvů můžou být vnořené. Běžný vnořený obor názvů má nekvalifikovaný přístup ke členům nadřazeného objektu, ale nadřazené členy nemají nekvalifikovaný přístup k vnořenému oboru názvů (pokud není deklarován jako vložený), jak je znázorněno v následujícím příkladu:

namespace ContosoDataServer
{
    void Foo();

    namespace Details
    {
        int CountImpl;
        void Ban() { return Foo(); }
    }

    int Bar(){...};
    int Baz(int i) { return Details::CountImpl; }
}

Běžné vnořené obory názvů lze použít k zapouzdření interních podrobností implementace, které nejsou součástí veřejného rozhraní nadřazeného oboru názvů.

Vložené obory názvů (C++11)

Na rozdíl od běžného vnořeného oboru názvů se členy vloženého oboru názvů považují za členy nadřazeného oboru názvů. Tato charakteristika umožňuje vyhledávání závislé na argumentech na přetížených funkcích pracovat s funkcemi, které mají přetížení v nadřazené a vnořené vložené obory názvů. Umožňuje také deklarovat specializace v nadřazenému oboru názvů pro šablonu, která je deklarována v vloženého oboru názvů. Následující příklad ukazuje, jak se externí kód ve výchozím nastavení sváže s vloženým oborem názvů:

// Header.h
#include <string>

namespace Test
{
    namespace old_ns
    {
        std::string Func() { return std::string("Hello from old"); }
    }

    inline namespace new_ns
    {
        std::string Func() { return std::string("Hello from new"); }
    }
}

// main.cpp
#include "header.h"
#include <string>
#include <iostream>

int main()
{
    using namespace Test;
    using namespace std;

    string s = Func();
    std::cout << s << std::endl; // "Hello from new"
    return 0;
}

Následující příklad ukazuje, jak můžete deklarovat specializace v nadřazené šabloně, která je deklarována v vloženého oboru názvů:

namespace Parent
{
    inline namespace new_ns
    {
         template <typename T>
         struct C
         {
             T member;
         };
    }
     template<>
     class C<int> {};
}

Vložené obory názvů můžete použít jako mechanismus správy verzí ke správě změn ve veřejném rozhraní knihovny. Můžete například vytvořit jeden nadřazený obor názvů a zapouzdřit každou verzi rozhraní ve vlastním oboru názvů vnořeném uvnitř nadřazeného objektu. Obor názvů, který obsahuje nejnovější nebo upřednostňovanou verzi, je kvalifikovaný jako vložený, a proto je vystaven jako přímý člen nadřazeného oboru názvů. Klientský kód, který vyvolá Parent::Class, se automaticky sváže s novým kódem. Klienti, kteří preferují použití starší verze, k němu stále mají přístup pomocí plně kvalifikované cesty k vnořenému oboru názvů, který má tento kód.

Vložené klíčové slovo musí být použito na první deklaraci oboru názvů v kompilační jednotce.

Následující příklad ukazuje dvě verze rozhraní, z nichž každý je vnořený obor názvů. Obor v_20 názvů má určité změny z v_10 rozhraní a je označen jako vložený. Klientský kód, který používá novou knihovnu a volání Contoso::Funcs::Add , vyvolá v_20 verzi. Kód, který se pokusí volat Contoso::Funcs::Divide , se teď zobrazí chyba doby kompilace. Pokud tuto funkci skutečně potřebují, můžou k verzi získat přístup v_10 explicitním voláním Contoso::v_10::Funcs::Divide.

namespace Contoso
{
    namespace v_10
    {
        template <typename T>
        class Funcs
        {
        public:
            Funcs(void);
            T Add(T a, T b);
            T Subtract(T a, T b);
            T Multiply(T a, T b);
            T Divide(T a, T b);
        };
    }

    inline namespace v_20
    {
        template <typename T>
        class Funcs
        {
        public:
            Funcs(void);
            T Add(T a, T b);
            T Subtract(T a, T b);
            T Multiply(T a, T b);
            std::vector<double> Log(double);
            T Accumulate(std::vector<T> nums);
        };
    }
}

Aliasy oboru názvů

Názvy oborů názvů musí být jedinečné, což znamená, že často by neměly být příliš krátké. Pokud délka názvu znesnadňuje čtení kódu nebo je zdlouhavé psát do souboru hlaviček, kde se nedají použít direktivy using, můžete vytvořit alias oboru názvů, který slouží jako zkratka skutečného názvu. Příklad:

namespace a_very_long_namespace_name { class Foo {}; }
namespace AVLNN = a_very_long_namespace_name;
void Bar(AVLNN::Foo foo){ }

anonymní nebo nepojmenované obory názvů

Explicitní obor názvů můžete vytvořit, ale ne pojmenovat ho:

namespace
{
    int MyFunc(){}
}

Tomu se říká nepojmenovaný nebo anonymní obor názvů a je užitečné, když chcete, aby deklarace proměnných byly neviditelné pro kód v jiných souborech (tj. dát jim interní propojení), aniž byste museli vytvořit pojmenovaný obor názvů. Veškerý kód ve stejném souboru může vidět identifikátory v nepojmenovaném oboru názvů, ale identifikátory spolu se samotným oborem názvů nejsou viditelné mimo daný soubor – nebo přesněji mimo jednotku překladu.

Viz také

Deklarace a definice