La spécification du langage permet aux implémentations d'implémenter <cmath>
en déclarant (et en définissant) les fonctions standard dans l' espace de noms global , puis en les amenant dans l'espace std
de noms au moyen de déclarations d'utilisation. Il n'est pas précisé si cette approche est utilisée
20.5.1.2 En-têtes
4 [...] Dans la bibliothèque standard C ++, cependant, les déclarations (à l'exception des noms qui sont définis comme des macros en C) sont dans la portée de l'espace de noms (6.3.6) de l'espace de noms std
. Il n'est pas précisé si ces noms (y compris les surcharges ajoutées dans les Articles 21 à 33 et dans l'Annexe D) sont d'abord déclarés dans la portée de l'espace de noms global et sont ensuite injectés dans l'espace std
de noms par des déclarations d'utilisation explicites (10.3.3).
Apparemment, vous avez affaire à l'une des implémentations qui a décidé de suivre cette approche (par exemple GCC). Ie votre mise en œuvre fournit ::abs
, tout std::abs
simplement "se réfère" à ::abs
.
Une question qui demeure dans ce cas est de savoir pourquoi en plus du standard ::abs
vous avez pu déclarer le vôtre ::abs
, c'est-à-dire pourquoi il n'y a pas d'erreur de définition multiple. Cela peut être causé par une autre fonctionnalité fournie par certaines implémentations (par exemple GCC): elles déclarent des fonctions standard comme des symboles dits faibles , vous permettant ainsi de les "remplacer" par vos propres définitions.
Ensemble, ces deux facteurs créent l'effet que vous observez: le remplacement d'un symbole faible de ::abs
entraîne également le remplacement de std::abs
. À quel point cela s'accorde avec la norme de la langue est une autre histoire ... Dans tous les cas, ne vous fiez pas à ce comportement - il n'est pas garanti par la langue.
Dans GCC, ce comportement peut être reproduit par l'exemple minimaliste suivant. Un fichier source
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Un autre fichier source
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
Dans ce cas, vous observerez également que la nouvelle définition de ::foo
( "Goodbye!"
) dans le deuxième fichier source affecte également le comportement de N::foo
. Les deux appels seront émis "Goodbye!"
. Et si vous supprimez la définition de ::foo
du deuxième fichier source, les deux appels seront envoyés à la définition «d'origine» ::foo
et à la sortie "Hello!"
.
L'autorisation donnée par le 20.5.1.2/4 ci-dessus est là pour simplifier la mise en œuvre de <cmath>
. Les implémentations sont autorisées à simplement inclure le style C <math.h>
, puis à redéclarer les fonctions std
et à ajouter des ajouts et des modifications spécifiques à C ++. Si l'explication ci-dessus décrit correctement la mécanique interne du problème, alors une grande partie de celui-ci dépend de la remplaçabilité des symboles faibles pour les versions de style C des fonctions.
Notez que si nous remplaçons simplement globalement int
par double
dans le programme ci-dessus, le code (sous GCC) se comportera "comme prévu" - il sortira -5 5
. Cela se produit parce que la bibliothèque standard C n'a pas de abs(double)
fonction. En déclarant le nôtre abs(double)
, nous ne remplaçons rien.
Mais si, après le passage de int
avec, double
nous passons également de abs
à fabs
, le comportement étrange d'origine réapparaîtra dans toute sa splendeur (sortie -5 -5
).
Ceci est cohérent avec l'explication ci-dessus.
abs
est incorrecte.