C++ Namespaces

Desde a primeira aula você escreve std::cout, std::string, std::vector. Esse std:: na frente é um namespace, um "sobrenome" que separa os nomes da biblioteca padrão dos seus. Quando um programa cresce, e principalmente quando junta código de várias pessoas e bibliotecas, dois pedaços acabam querendo o mesmo nome: duas funções preco, duas imprimir. Nesta aula você vai criar os seus próprios namespaces, entender o famoso using namespace std; e ver o erro real que ele causa.

Você vai aprender
  • Por que dois nomes iguais dão erro, e como um namespace resolve isso.
  • Como criar namespace loja { … } e usar loja::preco.
  • O que o std:: significa.
  • As três formas de encurtar: o nome completo, using loja::preco; e using namespace loja;.
  • O problema do using namespace std;: um nome seu colidindo com std::count ou std::distance.
  • Namespaces dentro de namespaces (loja::caixa), apelidos e o namespace anônimo.

Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.


A ideia: nome e sobrenome

Na turma da Ana há duas Marias. Quando a professora chama "Maria!", as duas olham. A solução é usar o sobrenome: "Maria Silva" e "Maria Souza". Ou pense em endereços: existe uma "Rua das Flores" em centenas de cidades, e ninguém se confunde, porque o endereço completo inclui a cidade.

No dia a diaNo C++
O sobrenome, a cidadeo namespace (em inglês, "espaço de nomes")
"Maria Silva", "Rua das Flores, Campinas"loja::preco, std::cout
Dentro de casa, todo mundo diz só "Maria"dentro do namespace, basta o nome curto

1. O problema: dois nomes iguais

A Ana vende na lojinha e também faz as contas da padaria do bairro. Cada uma tem sua tabela de preços, e as duas funções se chamam preco:

#include <iostream>

double preco(int codigo) { return codigo * 1.5; }      // da lojinha
double preco(int codigo) { return codigo * 2.0; }      // da padaria

int main() {
    std::cout << preco(10) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra conflito.cpp -o conflito
conflito.cpp:4:8: error: redefinition of ‘double preco(int)’
    4 | double preco(int codigo) { return codigo * 2.0; }      // da padaria
      |        ^~~~~
conflito.cpp:3:8: note: ‘double preco(int)’ previously defined here
    3 | double preco(int codigo) { return codigo * 1.5; }      // da lojinha
      |        ^~~~~

"Redefinição de double preco(int)": a nota mostra onde estava a primeira. Duas funções com o mesmo nome e os mesmos parâmetros não podem conviver no mesmo lugar (com parâmetros diferentes até poderiam: é a sobrecarga). Renomear para precoLoja e precoPadaria funciona aqui, mas não resolve o caso geral: quando o conflito é com uma biblioteca de outra pessoa, você não pode renomear o código dela.


2. Criando um namespace

#include <iostream>

namespace loja {
    double preco(int codigo) { return codigo * 1.5; }
}

namespace padaria {
    double preco(int codigo) { return codigo * 2.0; }
}

int main() {
    std::cout << "Loja: " << loja::preco(10) << '\n';
    std::cout << "Padaria: " << padaria::preco(10) << '\n';
}
namespace loja
Cria (ou reabre) um namespace chamado loja. O nome segue as regras de nomes de variáveis; o costume é tudo em minúsculas.
{ … }
Tudo o que for declarado entre as chaves ganha o "sobrenome" loja. Não vai ; depois da chave que fecha.
loja::preco(10)
Chama a preco do namespace loja. O :: se chama operador de escopo: leia como "o preco que mora em loja".
$ g++ -std=c++23 -Wall -Wextra loja.cpp -o loja && ./loja
Loja: 15
Padaria: 20

Agora as duas funções existem lado a lado: o nome completo de uma é loja::preco e o da outra é padaria::preco. Um namespace pode guardar qualquer coisa que tenha nome: funções, variáveis, constantes, structs, classes, outros namespaces.

"‘preco’ was not declared in this scope; did you mean ‘loja::preco’?"

Depois de pôr a função dentro do namespace, a Ana esqueceu de mudar a chamada:

#include <iostream>

namespace loja {
    double preco(int codigo) { return codigo * 1.5; }
}

int main() {
    std::cout << preco(10) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra sem.cpp -o sem
sem.cpp: In function ‘int main()’:
sem.cpp:8:18: error: ‘preco’ was not declared in this scope; did you mean ‘loja::preco’?
    8 |     std::cout << preco(10) << '\n';
      |                  ^~~~~
      |                  loja::preco
sem.cpp:4:12: note: ‘loja::preco’ declared here
    4 |     double preco(int codigo) { return codigo * 1.5; }
      |            ^~~~~

O que significa: fora do namespace, não existe nenhuma preco "solta". O g++ é gentil: pergunta "você quis dizer loja::preco?", mostra a correção embaixo do código e aponta onde ela foi declarada.

Como resolver: use o nome completo, loja::preco(10), ou um dos atalhos da seção 4.

Experimente: rode. Depois acrescente ao namespace padaria uma constante constexpr double TAXA = 0.05; e mostre padaria::TAXA no main. Por último, tente chamar preco(10) sem sobrenome e leia a mensagem.

#include <iostream>

namespace loja {
    double preco(int codigo) { return codigo * 1.5; }
}

namespace padaria {
    double preco(int codigo) { return codigo * 2.0; }
}

int main() {
    std::cout << "Loja: " << loja::preco(10) << '\n';
    std::cout << "Padaria: " << padaria::preco(10) << '\n';
}

3. O std é um namespace

Agora o std::cout faz sentido: cout mora no namespace std (de standard, "padrão"), onde fica toda a biblioteca padrão do C++: std::string, std::vector, std::sort, std::count… Sem o std::, o compilador não acha:

#include <iostream>

int main() {
    cout << "Olá!\n";
}
$ g++ -std=c++23 -Wall -Wextra semstd.cpp -o semstd
semstd.cpp: In function ‘int main()’:
semstd.cpp:4:5: error: ‘cout’ was not declared in this scope; did you mean ‘std::cout’?
    4 |     cout << "Olá!\n";
      |     ^~~~
      |     std::cout
In file included from semstd.cpp:1:
/usr/include/c++/15/iostream:65:18: note: ‘std::cout’ declared here
   65 |   extern ostream cout;          ///< Linked to standard output
      |                  ^~~~

A nota mostra até a linha do arquivo iostream onde o std::cout é declarado. A biblioteca padrão tem centenas de nomes, muitos deles palavras comuns em inglês (count, "contar"; distance, "distância"; size, "tamanho"; max, "máximo"). Guardá-los todos no std deixa os nomes "soltos" livres para você.


4. Três formas de chamar: qual usar

Escrever loja:: toda vez pode cansar. O C++ tem dois atalhos com a palavra using ("usando"):

FormaExemploO que fazRisco
Nome completoloja::preco(10)diz exatamente qualnenhum; só é mais comprido
Trazer um nome (using-declaration)using loja::preco;
depois: preco(10)
daqui em diante, preco quer dizer loja::precopequeno: você escolheu o nome, sabe que ele está ali
Trazer todos os nomes (using-directive)using namespace loja;
depois: preco(10)
todo nome de loja pode ser usado sem sobrenomegrande: traz nomes que você nem sabe que existem

Veja o risco do using namespace com os dois namespaces da Ana:

#include <iostream>

namespace loja    { double preco(int codigo) { return codigo * 1.5; } }
namespace padaria { double preco(int codigo) { return codigo * 2.0; } }

using namespace loja;
using namespace padaria;

int main() {
    std::cout << preco(10) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra dois.cpp -o dois
dois.cpp: In function ‘int main()’:
dois.cpp:10:23: error: call of overloaded ‘preco(int)’ is ambiguous
   10 |     std::cout << preco(10) << '\n';
      |                  ~~~~~^~~~
dois.cpp:10:23: note: there are 2 candidates
dois.cpp:3:28: note: candidate 1: ‘double loja::preco(int)’
    3 | namespace loja    { double preco(int codigo) { return codigo * 1.5; } }
      |                            ^~~~~
dois.cpp:4:28: note: candidate 2: ‘double padaria::preco(int)’
    4 | namespace padaria { double preco(int codigo) { return codigo * 2.0; } }
      |                            ^~~~~

"A chamada de preco(int) é ambígua": há dois candidatos (candidates) que servem igualmente bem, e o compilador se recusa a adivinhar. Os using namespace derrubaram as paredes que os namespaces tinham levantado. A saída é voltar ao nome completo (loja::preco(10)).


5. using namespace std;

Em muitos tutoriais e livros você vai ver, logo depois dos #include:

using namespace std;

Ela traz todos os nomes da biblioteca padrão para o seu arquivo, e aí dá para escrever cout, string e vector sem std::. Parece ótimo, até o dia em que um nome seu coincide com um dos centenas de nomes do std. A Ana guardou quantos itens há no carrinho numa variável chamada count ("contagem"):

#include <iostream>
using namespace std;

int count = 3;   // quantos itens há no carrinho

int main() {
    cout << "Itens: " << count << '\n';
}
$ g++ -std=c++23 -Wall -Wextra count.cpp -o count
count.cpp: In function ‘int main()’:
count.cpp:7:26: error: reference to ‘count’ is ambiguous
    7 |     cout << "Itens: " << count << '\n';
      |                          ^~~~~
In file included from /usr/include/c++/15/bits/unicode.h:39,
                 from /usr/include/c++/15/format:61,
                 from /usr/include/c++/15/ostream:44,
                 from /usr/include/c++/15/iostream:43,
                 from count.cpp:1:
/usr/include/c++/15/bits/stl_algo.h:4070:5: note: candidates are: ‘template<class _IIter, class _Tp> constexpr typename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&)’
 4070 |     count(_InputIterator __first, _InputIterator __last, const _Tp& __value)
      |     ^~~~~
count.cpp:4:5: note:                 ‘int count’
    4 | int count = 3;   // quantos itens há no carrinho
      |     ^~~~~

Assusta, mas dá para ler com calma:

error: reference to ‘count’ is ambiguous
"A referência a count é ambígua": existem dois count visíveis e o compilador não sabe qual você quer.
In file included from … iostream:43, from count.cpp:1
O caminho da colagem: o <iostream> inclui o <ostream>, que inclui o <format>… e lá no fundo vem o stl_algo.h. A Ana nem incluiu <algorithm>, e mesmo assim o std::count chegou.
note: candidates are: ‘template<…> … std::count(_IIter, _IIter, const _Tp&)’
Candidato 1: a função std::count da biblioteca padrão, que conta quantas vezes um valor aparece numa lista (você vai vê-la em Algoritmos). A parte com template é o jeito técnico de escrever o tipo dela; não precisa entender agora.
note: ‘int count’
Candidato 2: a variável da Ana, na linha 4.

Sem o using namespace std;, não haveria problema: o da biblioteca se chama std::count e o da Ana, count. O mesmo acontece com distance, outra palavra comum:

$ g++ -std=c++23 -Wall -Wextra distancia.cpp -o distancia
distancia.cpp: In function ‘int main()’:
distancia.cpp:7:30: error: reference to ‘distance’ is ambiguous
    7 |     cout << "Distância: " << distance << " km\n";
      |                              ^~~~~~~~
In file included from /usr/include/c++/15/bits/stl_construct.h:62,
                 from /usr/include/c++/15/bits/char_traits.h:59,
                 from /usr/include/c++/15/ios:44,
                 from /usr/include/c++/15/bits/ostream.h:43,
                 from /usr/include/c++/15/ostream:42,
                 from /usr/include/c++/15/iostream:43,
                 from distancia.cpp:1:
/usr/include/c++/15/bits/stl_iterator_base_funcs.h:150:5: note: candidates are: ‘template<class _InputIterator> constexpr typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator)’
  150 |     distance(_InputIterator __first, _InputIterator __last)
      |     ^~~~~~~~
distancia.cpp:4:5: note:                 ‘int distance’
    4 | int distance = 12;   // km até a padaria
      |     ^~~~~~~~

(O distancia.cpp é igual ao count.cpp, com int distance = 12; // km até a padaria na linha 4.) O pior desse problema é que ele pode aparecer sozinho: um programa que compilava hoje pode parar de compilar quando você atualizar o compilador e a biblioteca padrão ganhar um nome novo que coincide com um seu.

O conserto: traga só o que você usa

#include <iostream>
using std::cout;

int count = 3;   // quantos itens há no carrinho

int main() {
    cout << "Itens: " << count << '\n';
}
$ g++ -std=c++23 -Wall -Wextra count2.cpp -o count2 && ./count2
Itens: 3

using std::cout; traz só o cout. O std::count continua guardado no std, sem atrapalhar.

A regra deste guia
  • Escreva std:: por extenso. São cinco letras, e quem lê sabe na hora de onde vem cada nome.
  • Se quiser encurtar num .cpp, prefira using std::cout; (um nome por vez), depois dos #include.
  • using namespace std; é aceitável em programas pequenos de estudo, mas nunca num arquivo .h: o cabeçalho é colado em todos os arquivos que o incluem (veja Arquivos .h e .cpp), e o using iria junto, sem que eles pedissem.
  • Um using também pode ficar dentro de uma função; aí ele vale só entre as chaves dela.

Missão: este programa não compila por causa do using namespace std;. Rode e leia o ambiguous. Conserte sem renomear a variável: troque o using namespace std; por using só dos nomes que o programa usa (ou use std:: por extenso). A saída deve ser Itens: 3.

#include <iostream>
#include <algorithm>
using namespace std;

int count = 3;   // quantos itens há no carrinho

int main() {
    cout << "Itens: " << count << '\n';
}
#include <iostream>
#include <algorithm>
using std::cout;

int count = 3;   // quantos itens há no carrinho

int main() {
    cout << "Itens: " << count << '\n';
}

6. Namespaces dentro de namespaces

Um namespace pode ter outros dentro. Projetos grandes usam isso para organizar as partes: loja::caixa, loja::estoque.

#include <iostream>

namespace loja {
    namespace caixa {
        double troco(double pago, double total) { return pago - total; }
    }
}

namespace loja::estoque {
    int minimo = 5;
}

int main() {
    std::cout << "Troco: " << loja::caixa::troco(50, 32.5) << '\n';
    std::cout << "Estoque mínimo: " << loja::estoque::minimo << '\n';
    namespace lc = loja::caixa;
    std::cout << "Troco: " << lc::troco(20, 7) << '\n';
}
namespace loja { namespace caixa { … } }
O jeito clássico: um dentro do outro. O nome completo da função é loja::caixa::troco.
namespace loja::estoque { … }
O jeito curto, com os dois nomes de uma vez C++17. Faz o mesmo que escrever os dois namespace aninhados.
namespace lc = loja::caixa;
Um apelido (alias): daqui em diante, lc:: quer dizer loja::caixa::. Útil quando o nome é comprido, como o namespace fs = std::filesystem; que você vai ver em muito código.
$ g++ -std=c++23 -Wall -Wextra aninhado.cpp -o aninhado && ./aninhado
Troco: 17.5
Estoque mínimo: 5
Troco: 13

Repare que o namespace loja foi aberto duas vezes no mesmo arquivo, e tudo bem: um namespace pode ser reaberto quantas vezes for preciso, inclusive em arquivos diferentes. É assim que a biblioteca padrão põe coisas no std a partir de dezenas de cabeçalhos.

O namespace global e o :: sozinho

O que não está em namespace nenhum mora no namespace global. Para pedir explicitamente o de lá, use :: sem nada antes:

#include <iostream>

double preco(int codigo) { return codigo * 1.0; }

namespace loja {
    double preco(int codigo) { return codigo * 1.5; }

    void mostrar() {
        std::cout << "preco(10)   = " << preco(10) << '\n';
        std::cout << "::preco(10) = " << ::preco(10) << '\n';
    }
}

int main() {
    loja::mostrar();
}
$ g++ -std=c++23 -Wall -Wextra global.cpp -o global && ./global
preco(10)   = 15
::preco(10) = 10

Dentro do namespace loja, o nome curto preco acha primeiro o da própria loja (como "Maria", dito dentro de casa, é a Maria da família). O ::preco vai direto ao global.


7. Namespaces com .h e .cpp

Num projeto dividido em arquivos, o cabeçalho declara dentro do namespace e o .cpp reabre o mesmo namespace para definir:

#pragma once

namespace loja {
    double calcularTotal(double preco, int quantidade);
}
#include "calculos.h"

namespace loja {
    double calcularTotal(double preco, int quantidade) {
        return preco * quantidade;
    }
}

Quem inclui o calculos.h chama loja::calcularTotal(2.50, 4). Você vai montar exatamente isso na missão final.


8. O namespace anônimo: "só deste arquivo"

Às vezes uma função é só uma ajudante, usada dentro de um .cpp. A Ana tem uma linha() que desenha um traço no main.cpp e outra linha(), diferente, no relatorio.cpp. Cada arquivo compila bem sozinho, mas na ligação:

$ g++ -std=c++23 -Wall -Wextra main.cpp relatorio.cpp -o loja
/usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/ccOHoCsw.o: na função "linha()":
relatorio.cpp:(.text+0x0): múltiplas definições de "linha()"; /tmp/cc1uZyIO.o:main.cpp:(.text+0x0): definido primeiro aqui
collect2: error: ld returned 1 exit status

É o erro do ligador que você conheceu em Compilar Vários Arquivos: o programa inteiro só pode ter uma linha(). A solução é dizer que cada linha() é particular do seu arquivo, com um namespace sem nome:

#include <iostream>

namespace {
    void linha() { std::cout << "----------\n"; }
}

void relatorio();

int main() {
    linha();
    relatorio();
    linha();
}
#include <iostream>

namespace {
    void linha() { std::cout << "==========\n"; }
}

void relatorio() {
    linha();
    std::cout << "Relatório do dia\n";
    linha();
}
$ g++ -std=c++23 -Wall -Wextra main.cpp relatorio.cpp -o loja && ./loja
----------
==========
Relatório do dia
==========
----------
namespace { … }
Namespace anônimo (sem nome). Dentro do próprio arquivo, os nomes são usados normalmente, sem prefixo. Para os outros arquivos, eles são invisíveis.
o resultado
Cada .cpp tem a sua linha() particular, e o ligador não vê conflito: o main usou o traço - e o relatório usou o =.

O nm mostra a diferença: a letra é t minúscula, que quer dizer "definida aqui, mas só para uso local".

$ g++ -std=c++23 -Wall -Wextra -c main.cpp && nm -C main.o | grep linha
0000000000000000 t (anonymous namespace)::linha()

Em código antigo, o mesmo efeito é obtido escrevendo static antes da função (static void linha() { … }). Em C++ moderno, prefira o namespace anônimo, que vale também para structs e classes. Use-o para toda ajudante que não aparece no .h, e nunca dentro de um .h (cada arquivo que o incluísse ganharia uma cópia particular).


Referência

EscritaO que faz
namespace loja { … }cria ou reabre o namespace loja
loja::precoo preco de dentro da loja
::precoo preco do namespace global
using loja::preco;traz um nome
using namespace loja;traz todos os nomes (evite com std e nos .h)
namespace loja { namespace caixa { … } }namespace aninhado
namespace loja::caixa { … }o mesmo, forma curta C++17
namespace lc = loja::caixa;apelido
namespace { … }anônimo: só deste .cpp
MensagemCausa provável
‘x’ was not declared in this scope; did you mean ‘loja::x’?faltou o sobrenome (loja:: ou std::)
reference to ‘count’ is ambiguousum nome seu igual a um do std, com using namespace std;
call of overloaded ‘f(int)’ is ambiguousdois using namespace trazendo funções iguais
redefinition of ‘double preco(int)’duas definições iguais no mesmo namespace

Exercícios

1. Chamar pelo nome completo

A função desconto está no namespace loja. Complete a chamada:

(100)

2. Trazer um nome só

Complete para poder escrever cout sem std::, sem trazer o resto da biblioteca padrão:

3. Com using namespace std;, a Ana criou int count = 0; fora do main e recebeu reference to ‘count’ is ambiguous. Por quê?

4. Para que serve um namespace anônimo (namespace { … })?

5. Qual é o nome completo de minimo em namespace loja { namespace estoque { int minimo = 5; } }?

Missão final: a padaria ganha o seu namespace

Missão: a lojinha já tem o namespace loja, declarado no precos.h e definido no precos.cpp. Acrescente um namespace padaria com uma função double preco(int codigo) que devolve codigo * 2.0: declare no precos.h, defina no precos.cpp e mostre padaria::preco(10) no main.cpp. A saída deve ser:

Loja: 15
Padaria: 20
#include <iostream>
#include "precos.h"

int main() {
    std::cout << "Loja: " << loja::preco(10) << '\n';
    // mostre aqui o preço da padaria
}
#pragma once

namespace loja {
    double preco(int codigo);
}
#include "precos.h"

namespace loja {
    double preco(int codigo) {
        return codigo * 1.5;
    }
}
#include <iostream>
#include "precos.h"

int main() {
    std::cout << "Loja: " << loja::preco(10) << '\n';
    std::cout << "Padaria: " << padaria::preco(10) << '\n';
}
#pragma once

namespace loja {
    double preco(int codigo);
}

namespace padaria {
    double preco(int codigo);
}
#include "precos.h"

namespace loja {
    double preco(int codigo) {
        return codigo * 1.5;
    }
}

namespace padaria {
    double preco(int codigo) {
        return codigo * 2.0;
    }
}

Resumo

  • Um namespace é um sobrenome para nomes: loja::preco e padaria::preco convivem sem conflito.
  • :: separa o namespace do nome. std é o namespace da biblioteca padrão; ::x sozinho é o global.
  • using loja::preco; traz um nome; using namespace loja; traz todos, e pode causar ambiguous.
  • using namespace std; mistura os seus nomes com centenas da biblioteca: count e distance dão reference to … is ambiguous. Prefira std:: por extenso ou using std::cout;, e nunca ponha using namespace num .h.
  • Namespaces podem ser aninhados (loja::caixa, ou namespace loja::caixa no C++17), reabertos em vários arquivos e ganhar apelidos (namespace lc = loja::caixa;).
  • namespace { … } deixa as ajudantes de um .cpp particulares a ele, sem conflito na ligação.