A especificação da linguagem permite que as implementações sejam implementadas <cmath>
declarando (e definindo) as funções padrão no namespace global e, em seguida, trazendo-as para o namespace std
por meio de declarações de uso. Não é especificado se esta abordagem é usada
20.5.1.2 Cabeçalhos
4 Na biblioteca padrão C ++, entretanto, as declarações (exceto para nomes que são definidos como macros em C) estão dentro do escopo do namespace (6.3.6) do namespace std
. Não é especificado se esses nomes (incluindo quaisquer sobrecargas adicionadas nas Cláusulas 21 a 33 e no Anexo D) são primeiro declarados no escopo do namespace global e, em seguida, injetados no namespace std
por declarações de uso explícitas (10.3.3).
Aparentemente, você está lidando com uma das implementações que decidiu seguir essa abordagem (por exemplo, GCC). Ou seja, sua implementação fornece ::abs
, enquanto std::abs
simplesmente "se refere" a ::abs
.
Uma questão que permanece neste caso é por que, além do padrão, ::abs
você foi capaz de declarar o seu próprio ::abs
, ou seja, por que não há erro de definição múltipla. Isso pode ser causado por outro recurso fornecido por algumas implementações (por exemplo, GCC): eles declaram funções padrão como os chamados símbolos fracos , permitindo assim que você "substitua" por suas próprias definições.
Esses dois fatores juntos criam o efeito que você observa: a substituição do símbolo fraco de ::abs
também resulta na substituição de std::abs
. O quão bem isso está de acordo com o padrão da linguagem é uma história diferente ... Em qualquer caso, não confie neste comportamento - não é garantido pela linguagem.
No GCC, esse comportamento pode ser reproduzido pelo seguinte exemplo minimalista. Um arquivo de origem
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Outro arquivo fonte
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
Nesse caso, você também observará que a nova definição de ::foo
( "Goodbye!"
) no segundo arquivo de origem também afeta o comportamento de N::foo
. Ambas as chamadas serão geradas "Goodbye!"
. E se você remover a definição de ::foo
do segundo arquivo de origem, ambas as chamadas serão enviadas para a definição "original" de ::foo
e para a saída "Hello!"
.
A permissão dada pelo 20.5.1.2/4 acima existe para simplificar a implementação do <cmath>
. As implementações podem simplesmente incluir o estilo C <math.h>
, depois declarar novamente as funções std
e adicionar algumas adições e ajustes específicos de C ++. Se a explicação acima descreve adequadamente a mecânica interna do problema, a maior parte dela depende da capacidade de substituição de símbolos fracos para versões de estilo C das funções.
Observe que se nós simplesmente substituirmos globalmente int
por double
no programa acima, o código (sob GCC) se comportará "como esperado" - ele será impresso -5 5
. Isso acontece porque a biblioteca padrão C não tem abs(double)
função. Ao declarar o nosso abs(double)
, não substituímos nada.
Mas se depois de alternar de int
com double
também mudarmos de abs
para fabs
, o comportamento estranho original reaparecerá em toda sua glória (saída -5 -5
).
Isso é consistente com a explicação acima.
abs
está incorreta.