#include <iostream>
#include <cmath>
/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}
int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}
Je m'attendais à ce que la sortie soit -5
et 5
, mais la sortie est le -5
et -5
.
Je me demande pourquoi ce cas se produira?
Cela a-t-il quelque chose à voir avec l'utilisation std
ou quoi?
abs
est incorrecte.abs
effets brisésstd::abs()
.5
et5
avec clang,-5
et-5
avec gcc.return 0
- cela aurait évité que les gens pensent que vous avez involontairement implémenté la fonction de manière incorrecte et clarifié le comportement souhaité et réel.Réponses:
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'espacestd
de noms au moyen de déclarations d'utilisation. Il n'est pas précisé si cette approche est utiliséeApparemment, 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
, toutstd::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 destd::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
Un autre fichier source
Dans ce cas, vous observerez également que la nouvelle définition de
::foo
("Goodbye!"
) dans le deuxième fichier source affecte également le comportement deN::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 fonctionsstd
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
pardouble
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 deabs(double)
fonction. En déclarant le nôtreabs(double)
, nous ne remplaçons rien.Mais si, après le passage de
int
avec,double
nous passons également deabs
à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.
la source
using ::abs;
pareil pour leusing ::asin;
donc Vous pouvez remplacer la déclaration, un autre point à mentionner est que les fonctions d'espace de noms définies dans std ne sont pas déclarées pour int mais plutôt pour double , float#include<cmath>
dans mon code, j'ai eu la même réponse. »abs
peut également être déclaré dans<cstdlib>
, ce qui peut être implicitement inclus via<iostream>
. Essayez de supprimer le vôtreabs
et de voir s'il se compile toujours.Votre code provoque un comportement indéfini.
C ++ 17 [extern.names] / 4:
Vous ne pouvez donc pas faire fonction avec le même prototype que la fonction de la bibliothèque standard C
int abs(int);
. Quels que soient les en-têtes que vous incluez réellement ou si ces en-têtes placent également les noms de bibliothèques C dans l'espace de noms global.Cependant, il serait autorisé à surcharger
abs
si vous fournissez différents types de paramètres.la source