#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;
}
Ich habe erwartet, dass die Ausgabe -5
und sein wird 5
, aber die Ausgabe ist die -5
und -5
.
Ich frage mich, warum dieser Fall passieren wird.
Hat es etwas mit der Verwendung von std
oder was zu tun ?
abs
ist falsch.abs
Effektestd::abs()
.5
und5
mit Klirren-5
und-5
mit gcc.return 0
- das hätte vermieden, dass die Leute glauben, Sie hätten die Funktion unbeabsichtigt falsch implementiert und das gewünschte und tatsächliche Verhalten klarer gemacht.Antworten:
Die Sprachspezifikation ermöglicht die Implementierung von Implementierungen,
<cmath>
indem die Standardfunktionen im globalen Namespace deklariert (und definiert) und dannstd
mithilfe von using-Deklarationen in den Namespace gebracht werden. Es ist nicht spezifiziert, ob dieser Ansatz verwendet wirdAnscheinend haben Sie es mit einer der Implementierungen zu tun, die sich für diesen Ansatz entschieden haben (z. B. GCC). Dh Ihre Implementierung bietet
::abs
, währendstd::abs
einfach "bezieht"::abs
.Eine Frage, die in diesem Fall noch offen ist, ist, warum Sie zusätzlich zu dem Standard
::abs
Ihren eigenen deklarieren konnten::abs
, dh warum es keinen Mehrfachdefinitionsfehler gibt. Dies kann durch eine andere Funktion verursacht werden, die von einigen Implementierungen bereitgestellt wird (z. B. GCC): Sie deklarieren Standardfunktionen als sogenannte schwache Symbole , sodass Sie sie durch Ihre eigenen Definitionen "ersetzen" können.Diese beiden Faktoren zusammen erzeugen den Effekt, den Sie beobachten: Das Ersetzen von schwachen Symbolen führt
::abs
auch zum Ersetzen vonstd::abs
. Wie gut dies mit dem Sprachstandard übereinstimmt, ist eine andere Geschichte ... Verlassen Sie sich auf keinen Fall auf dieses Verhalten - es wird von der Sprache nicht garantiert.In GCC kann dieses Verhalten anhand des folgenden minimalistischen Beispiels reproduziert werden. Eine Quelldatei
Eine andere Quelldatei
In diesem Fall werden Sie auch feststellen, dass die neue Definition von
::foo
("Goodbye!"
) in der zweiten Quelldatei auch das Verhalten von beeinflusstN::foo
. Beide Anrufe werden ausgegeben"Goodbye!"
. Wenn Sie die Definition von::foo
aus der zweiten Quelldatei entfernen , werden beide Aufrufe an die "ursprüngliche" Definition von::foo
ausgegeben und ausgegeben"Hello!"
.Die Erlaubnis des obigen 20.5.1.2/4 dient dazu, die Implementierung von zu vereinfachen
<cmath>
. Implementierungen dürfen einfach C-Stil enthalten<math.h>
, dann die Funktionen neu deklarierenstd
und einige C ++ - spezifische Ergänzungen und Optimierungen hinzufügen. Wenn die obige Erklärung die innere Mechanik des Problems richtig beschreibt, hängt ein Großteil davon von der Ersetzbarkeit schwacher Symbole für C-Versionen der Funktionen ab.Beachten Sie, dass , wenn wir einfach global ersetzen
int
mitdouble
in dem obigen Programm wird der Code (unter GCC) „wie erwartet“ verhalten - es wird ausgegeben-5 5
. Dies geschieht, weil die C-Standardbibliothek keineabs(double)
Funktion hat. Indemabs(double)
wir unsere eigenen deklarieren , ersetzen wir nichts.Wenn wir aber nach dem Wechsel von
int
mitdouble
auch vonabs
zu wechseln ,fabs
wird das ursprüngliche seltsame Verhalten in seiner vollen Pracht (Ausgabe-5 -5
) wieder auftauchen .Dies steht im Einklang mit der obigen Erklärung.
quelle
using ::abs;
für das.using ::asin;
Sie können die Deklaration überschreiben. Ein weiterer zu erwähnender Punkt ist, dass die in std definierten Namespace-Funktionen nicht für int deklariert sind, sondern für double , float#include<cmath>
in meinem Code löschte , bekam ich die gleiche Antwort. "abs
kann auch in deklariert werden<cstdlib>
, was implizit durch eingeschlossen sein kann<iostream>
. Versuchen Sie, Ihre eigenen zu entfernenabs
und zu prüfen, ob sie noch kompiliert werden.Ihr Code verursacht undefiniertes Verhalten.
C ++ 17 [extern.names] / 4:
Sie können also keine Funktion mit demselben Prototyp wie die Standard C-Bibliotheksfunktion erstellen
int abs(int);
. Unabhängig davon, welche Header Sie tatsächlich einschließen oder ob diese Header auch C-Bibliotheksnamen in den globalen Namespace einfügen.Es kann jedoch zu einer Überlastung kommen,
abs
wenn Sie unterschiedliche Parametertypen angeben.quelle