#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; }
I expected that the output will be -5
and 5
, but the output is the -5
and -5
.
I wonder why this case will happen?
Does it have anything to do with the use of std
or what?
A namespace is a declarative region that provides a scope to the identifiers (names of functions, variables or other user-defined data types) inside it. Multiple namespace blocks with the same name are allowed. All declarations within those blocks are declared in the named scope.
While this practice is okay for example code, pulling in the entire std namespace into the global namespace is not good as it defeats the purpose of namespaces and can lead to name collisions. This situation is called namespace pollution.
So when we run a program to print something, “using namespace std” says if you find something that is not declared in the current scope go and check std. using namespace std; are used. It is because computer needs to know the code for the cout, cin functionalities and it needs to know which namespace they are defined.
It doesn't affect the runtime performance at all.
The language specification allows implementations to implement <cmath>
by declaring (and defining) the standard functions in global namespace and then bringing them into namespace std
by means of using-declarations. It is unspecified whether this approach is used
20.5.1.2 Headers
4 [...] In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (6.3.6) of the namespacestd
. It is unspecified whether these names (including any overloads added in Clauses 21 through 33 and Annex D) are first declared within the global namespace scope and are then injected into namespacestd
by explicit using-declarations (10.3.3).
Apparently, you are dealing with one of implementations that decided to follow this approach (e.g. GCC). I.e. your implementation provides ::abs
, while std::abs
simply "refers" to ::abs
.
One question that remains in this case is why in addition to the standard ::abs
you were able to declare your own ::abs
, i.e. why there's no multiple definition error. This might be caused by another feature provided by some implementations (e.g. GCC): they declare standard functions as so called weak symbols, thus allowing you to "replace" them with your own definitions.
These two factors together create the effect you observe: weak-symbol replacement of ::abs
also results in replacement of std::abs
. How well this agrees with the language standard is a different story... In any case, don't rely on this behavior - it is not guaranteed by the language.
In GCC this behavior can be reproduced by the following minimalistic example. One source file
#include <iostream> void foo() __attribute__((weak)); void foo() { std::cout << "Hello!" << std::endl; }
Another source file
#include <iostream> void foo(); namespace N { using ::foo; } void foo() { std::cout << "Goodbye!" << std::endl; } int main() { foo(); N::foo(); }
In this case you will also observe that the new definition of ::foo
("Goodbye!"
) in the second source file also affects the behavior of N::foo
. Both calls will output "Goodbye!"
. And if you remove the definition of ::foo
from the second source file, both calls will dispatch to the "original" definition of ::foo
and output "Hello!"
.
The permission given by the above 20.5.1.2/4 is there to simplify implementation of <cmath>
. Implementations are allowed to simply include C-style <math.h>
, then redeclare the functions in std
and add some C++-specific additions and tweaks. If the above explanation properly describes the inner mechanics of the issue, then a major part of it depends on replaceability of weak symbols for C-style versions of the functions.
Note that if we simply globally replace int
with double
in the above program, the code (under GCC) will behave "as expected" - it will output -5 5
. This happens because C standard library does not have abs(double)
function. By declaring our own abs(double)
, we do not replace anything.
But if after switching from int
with double
we also switch from abs
to fabs
, the original weird behavior will reappear in its full glory (output -5 -5
).
This is consistent with the above explanation.
Your code causes undefined behaviour.
C++17 [extern.names]/4:
Each function signature from the C standard library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.
So you cannot make a function with the same prototype as the Standard C library function int abs(int);
. Regardless of which headers you actually include or whether those headers also put C library names into the global namespace.
However, it would be allowed to overload abs
if you provide different parameter types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With