Usando namespaces

por Fabio A. Mazzarino

Muita gente usa namespace e não sabe exatamente nem por quê, nem como funciona. Namespaces é uma forma de isolar identificadores com contexto global, evitando o conflito de nomes. Assim podemos criar novos identificadores sem nenhuma preocupação se eles estão em uso por parte de outro trecho do código, ou por outra biblioteca.

Declaração

A declaração de um namespace é bem simples., basta utilizar a palavra chave namespace, seguida do nome do namespace e chaves. Tudo o que for declarado dentro das chaves vai estar contido dentro do namespace, e para acessá-los de fora é preciso utilizar o operador ::, ou através do using namespace.

Um bom exemplo seria uma biblioteca de containers equivalente à STL, porém certificada para um fim específico:

namespace cert {
    template < class T, class Alloc = allocator<T> > 
    class list {
        /* ... */
    }
}

Dessa forma a classe list declarada dentro do namespace cert é diferente da declarada dentro do namespace std, e ambas podem conviver pacificamente, sem conflitos, dentro da mesma base de código.

É possível também criar um namespace anônimo. Neste caso o efeito é o mesmo de declarar uma variável global como estática: somente estará acessível dentro do próprio arquivo. Por exemplo, o código abaixo vai dar erro de linkagem porque o acesso à variável x em file2.cpp não será permitido.

file1.cpp
    namespace {
        int x;
    }
    void f1() { x = 1; }

file2.cpp
    extern int x;   // compila ok
    void f2() {
        x = 2;      // falha na linkagem
    }

É possível também definir namespaces aninhados:

namespace cert {
    int x;
    namespace do178 {
        int y;
    }
}
int main() {
    int x = cert::x;
    int y = cert::do178::y;
}

Utilização

No exemplo da classe list contida no namespace cert temos duas opções para utilizá-la. A primeira é acessar o nome da classe utilizando o nome do namespace:

#include <cert/list>
int main() {
    cert::list<int> l;
    /* ... */
}

A outra forma é utilizando using:

#include <cert/list>
using namespace cert;
int main() {
    list<int> l;
    /* ... */
}

Que é a forma mais popular para utilizar o namespace std, padrão do STL. A maioria dos desenvolvedores utiliza using namespace std; para acessar as classes do STL, mas não sabia exatamente o porquê. Como no exemplo:

#include <iostream>
using namespace std;
int main() {
    cout << "Testing..." << endl;
}

É ainda possível utilizar somente uma entidade membro do namespace, reduzindo ou eliminando a chance de conflito. Por exemplo:

#include <iostream>
#include <cert/list>
using std::cout;
using cert::list;
int main() {
    cout << "using " << std::endl;
    list<int> l;
    /* ... */
}

Note que se forem utilizados os dois namespaces em questão, std e cert, haverá conflito nos nomes das classes, gerando um erro.

Quando Utilizar Cada Opção?

Sempre que o assunto for estilo, a decisão final sempre fica a cargo da equipe. Cada equipe define a melhor forma de declarar e principalmente utilizar namespaces. A grande vantagem de utilizar using é reduzir a quantidade de código a ser digitado. Outra vantagem é poder trocar entre namespaces equivalentes, através da alteração de uma única linha em cada arquivo.

Por outro lado utilizar o identificador completo, com todos os namespaces, deixa explicito sobre qual namespace a declaração se trata. Portanto é mais indicado quando é necessário ser mais didático, pois indica com exatidão em quais namespaces as delcarações pertencem.

No Lab C++ não é utilizado using, pelo fato que é necessário ser didático e deixar bem claro os namespaces, portanto não estranhe quando ver o seguinte código:

std::cout << 
    std::setfill('.') << std::setw(20) << std::left << 
    it->first << 
" $ " << 
    std::setfill(' ') << std::setw(8) << std::right << 
    
std::fixed << std::setprecision(2) << 
    it->second << 
    std::endl;

Apesar de ficar um pouco maior que o necessário ninguém fica na dúvida do namespace.