La specifica del linguaggio consente l' implementazione delle implementazioni <cmath>
dichiarando (e definendo) le funzioni standard nello spazio dei nomi globale e quindi portandole nello spazio std
dei nomi mediante dichiarazioni-utilizzo. Non è specificato se questo approccio viene utilizzato
20.5.1.2 Intestazioni
4 [...] Nella libreria standard C ++, tuttavia, le dichiarazioni (ad eccezione dei nomi che sono definiti come macro in C) rientrano nell'ambito dello spazio dei nomi (6.3.6) dello spazio dei nomi std
. Non è specificato se questi nomi (inclusi eventuali sovraccarichi aggiunti nelle clausole da 21 a 33 e nell'Allegato D) siano prima dichiarati all'interno dell'ambito dello spazio dei nomi globale e siano poi iniettati nello spazio std
dei nomi da dichiarazioni using esplicite (10.3.3).
Apparentemente, hai a che fare con una delle implementazioni che hanno deciso di seguire questo approccio (ad es. GCC). Cioè la tua implementazione fornisce ::abs
, mentre std::abs
semplicemente "si riferisce" a ::abs
.
Una domanda che rimane in questo caso è perché oltre allo standard ::abs
sei stato in grado di dichiarare il tuo ::abs
, cioè perché non c'è errore di definizione multipla. Ciò potrebbe essere causato da un'altra caratteristica fornita da alcune implementazioni (es. GCC): dichiarano le funzioni standard come i cosiddetti simboli deboli , consentendo così di "sostituirle" con le proprie definizioni.
Questi due fattori insieme creano l'effetto che osservi: la sostituzione del simbolo debole di ::abs
comporta anche la sostituzione di std::abs
. Quanto questo concordi con lo standard linguistico è un'altra storia ... In ogni caso, non fare affidamento su questo comportamento - non è garantito dalla lingua.
In GCC questo comportamento può essere riprodotto dal seguente esempio minimalista. Un file sorgente
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Un altro file sorgente
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
In questo caso osserverai anche che la nuova definizione di ::foo
( "Goodbye!"
) nel secondo file sorgente influenza anche il comportamento di N::foo
. Entrambe le chiamate verranno emesse "Goodbye!"
. E se rimuovi la definizione di ::foo
dal secondo file sorgente, entrambe le chiamate verranno inviate alla definizione "originale" ::foo
e all'output "Hello!"
.
L'autorizzazione data dal precedente 20.5.1.2/4 serve a semplificare l'implementazione di <cmath>
. Le implementazioni possono includere semplicemente lo stile C <math.h>
, quindi dichiarare nuovamente le funzioni std
e aggiungere alcune aggiunte e modifiche specifiche per C ++. Se la spiegazione di cui sopra descrive adeguatamente i meccanismi interni del problema, una parte importante di essa dipende dalla sostituibilità dei simboli deboli per le versioni in stile C delle funzioni.
Nota che se sostituiamo globalmente int
con double
nel programma precedente, il codice (sotto GCC) si comporterà "come previsto" - verrà visualizzato -5 5
. Ciò accade perché la libreria standard C non ha abs(double)
funzioni. Dichiarando il nostro abs(double)
, non sostituiamo nulla.
Ma se dopo il passaggio da int
con double
si passa anche da abs
a fabs
, il comportamento strano originale riapparirà nella sua piena gloria (output -5 -5
).
Ciò è coerente con la spiegazione di cui sopra.
abs
non è corretta.