Namespaces (C++)

Um namespace é uma região declarativa que fornece um escopo para os identificadores (os nomes de tipos, funções, variáveis etc.) dentro dele. Namespaces são usados para organizar código em grupos lógicos e evitar colisões de nome que podem ocorrer especialmente quando sua base de código inclui várias bibliotecas. Todos os identificadores no escopo do namespace ficam visíveis uns para os outros sem qualificação. Identificadores fora do namespace podem acessar os membros usando o nome totalmente qualificado para cada identificador, por exemplo, std::vector<std::string> vec;, ou uma Declaração using para um só identificador (using std::string) ou uma Diretiva using para todos os identificadores no namespace (using namespace std;). O código nos arquivos de cabeçalho sempre deve usar o nome do namespace totalmente qualificado.

O exemplo a seguir mostra uma declaração de namespace e três maneiras pelas quais o código fora do namespace pode acessar seus membros.

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

Usar o nome totalmente qualificado:

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

Usar uma declaração using para colocar um identificador no escopo:

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

Usar uma diretiva using para colocar tudo no namespace no escopo:

using namespace ContosoData;

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

Diretivas using

A diretiva using permite que os nomes em um namespace sejam usados sem o namespace-name como qualificador explícito. Use uma diretiva using em um arquivo de implementação (ou seja, *.cpp) se você estiver usando vários identificadores diferentes em um namespace; se você estiver usando apenas um ou dois identificadores, considere uma declaração using para colocar apenas esses identificadores no escopo, e não todos os identificadores no namespace. Se uma variável local tiver o mesmo nome de uma variável de namespace, a variável de namespace será oculta. É um erro ter uma variável de namespace com o mesmo nome de uma variável global.

Observação

Uma diretiva using pode ser colocada na parte superior de um arquivo .cpp (no escopo do arquivo) ou dentro de uma classe ou definição de função.

Em geral, evite colocar diretivas using em arquivos de cabeçalho (*.h) porque qualquer arquivo que inclua esse cabeçalho colocará tudo no namespace no escopo, o que pode causar problemas de ocultação de nome e colisão de nome que são muito difíceis de depurar. Sempre use nomes totalmente qualificados em um arquivo de cabeçalho. Se esses nomes ficarem muito longos, você poderá usar um alias de namespace para encurtá-los. (Consulte abaixo.)

Como declarar namespaces e membros do namespace

Normalmente, você declara um namespace em um arquivo de cabeçalho. Se suas implementações de função estiverem em um arquivo separado, qualifique os nomes de função, como neste exemplo.

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

As implementações de função em contosodata.cpp devem usar o nome totalmente qualificado, mesmo se você colocar uma diretiva using na parte superior do arquivo:

#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;}

Um namespace pode ser declarado em vários blocos em um único arquivo e em vários arquivos. O compilador une as partes durante o pré-processamento e o namespace resultante contém todos os membros declarados em todas as partes. Um exemplo disso é o namespace std, que é declarado em cada um dos arquivos de cabeçalho na biblioteca padrão.

Membros de um namespace nomeado podem ser definidos fora do namespace no qual são declarados pela qualificação explícita do nome que está sendo definido. Porém, a definição deve aparecer após o ponto de declaração em um namespace que inclui o namespace da declaração. Por exemplo:

// 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();
}

Esse erro pode ocorrer quando os membros do namespace são declarados em vários arquivos de cabeçalho e você não incluiu esses cabeçalhos na ordem correta.

O namespace global

Se um identificador não for declarado em um namespace explícito, ele fará parte do namespace global implícito. Em geral, tente evitar fazer declarações no escopo global quando possível, exceto para a Função main do ponto de entrada, que é deve no namespace global. Para qualificar explicitamente um identificador global, use o operador de resolução de escopo sem nome, como em ::SomeFunction(x);. Isso diferenciará o identificador de qualquer coisa com o mesmo nome em qualquer outro namespace e também ajudará a facilitar a compreensão do código.

O namespace std

Todos os tipos e funções de biblioteca padrão C++ são declarados no namespace stdou em namespaces aninhados dentro de std.

Namespaces aninhados

Os namespaces podem ser aninhados. Um namespace aninhado comum tem acesso não qualificado aos membros de seus pais, mas os membros pai não têm acesso não qualificado ao namespace aninhado (a menos que seja declarado como embutido), conforme mostra o seguinte exemplo:

namespace ContosoDataServer
{
    void Foo();

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

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

Namespaces aninhados comuns podem ser usados para encapsular detalhes de implementação interna que não fazem parte da interface pública do namespace pai.

Namespaces embutidos (C++11)

Em contraste com um namespace aninhado comum, os membros de um namespace embutido são tratados como membros do namespace pai. Essa característica permite que a pesquisa dependente de argumento em funções sobrecarregadas funcione em funções que têm sobrecargas em um namespace embutido e pai. Também permite que você declare uma especialização em um namespace pai para um modelo declarado no namespace embutido. O seguinte exemplo mostra como o código externo é associado ao namespace embutido por padrão:

// 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;
}

O seguinte exemplo mostra como você pode declarar uma especialização em um pai de um modelo declarado em um namespace embutido:

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

Você pode usar namespaces embutidos como um mecanismo de controle de versão para gerenciar alterações à interface pública de uma biblioteca. Por exemplo, você pode criar um namespace pai único e encapsular cada versão da interface em seu namespace aninhado dentro do pai. O namespace que contém a versão mais recente ou preferencial é qualificado como embutido, portanto, é exposto como se fosse um membro direto do namespace pai. O código do cliente que invoca o Parent::Class será automaticamente associado ao novo código. Os clientes que preferem usar a versão mais antiga ainda podem acessá-la usando o caminho totalmente qualificado para o namespace aninhado que tem esse código.

A palavra-chave embutida deve ser aplicada à primeira declaração do namespace em uma unidade de compilação.

O exemplo a seguir mostra duas versões de uma interface, cada uma em um namespace aninhado. O namespace v_20 tem alguma modificação da interface v_10 e é marcado como embutido. O código do cliente que usa a nova biblioteca e chama Contoso::Funcs::Add invocará a versão v_20. O código que tenta chamar Contoso::Funcs::Divide agora receberá um erro em tempo de compilação. Se eles realmente precisarem dessa função, ainda poderão acessar a versão v_10 chamando Contoso::v_10::Funcs::Divide explicitamente.

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);
        };
    }
}

aliases de namespace

Os nomes de namespace precisam ser exclusivos, o que significa que, muitas vezes, não devem ser muito curtos. Se o comprimento de um nome dificultar a leitura do código ou for entediante digitar um arquivo de cabeçalho em que não é possível usar diretivas using, crie um alias de namespace para servir como uma abreviação do nome real. Por exemplo:

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

namespaces anônimos ou sem nome

Você pode criar um namespace explícito, mas não dar um nome a ele:

namespace
{
    int MyFunc(){}
}

Isso é chamado de namespace anônimo ou sem nome e é útil quando você quer tornar as declarações variáveis invisíveis ao código em outros arquivos (ou seja, fornecer a elas vínculo interno) sem precisar criar um namespace nomeado. Todos os códigos no mesmo arquivo podem ver os identificadores em um namespace sem nome, mas os identificadores, com o namespace em si, não são visíveis fora desse arquivo ou, mais precisamente, fora da unidade de tradução.

Confira também

Declarações e definições