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 namespace std
. 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 namespace std
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.
abs
is incorrect. – Nucleolusabs
affectsstd::abs()
. – Rosenkrantzabs
like this is not allowed – Cabral5
and5
with clang,-5
and-5
with gcc. – Lipoid-5
and-5
. – Greyhenreturn 0
- that would've avoided having people think you unintentionally implemented the function incorrectly and made the desired and actual behaviour clearer. – Decennium