In the C++ standard section 17.6.1 Library contents and organization, we read in 17.6.1.2:
Except as noted in Clauses 18 through 30 and Annex D, the contents of
each header cname shall be the same as that of the corresponding
header name.h , as specified in the C standard library (1.2) or the C
Unicode TR, as appropriate, as if by inclusion. In the C
++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the
namespace std. It is unspecified whether these names are first
declared within the global namespace scope and are then injected into
namespace std by explicit using-declarations (7.3.3).
emphasis added
Additionally, in 17.6.4.3.2 External linkage we read
Each name from the Standard C library declared with external linkage
is reserved to the implementation for use as a name with extern "C"
linkage, both in namespace std and in the global namespace
In plain English from this section and similar, C standard library names are reserved, but C standard library names are only in the global namespace scope.
What GLIBCXX is doing here is perfectly valid; it's declaring an abs in the global namespace scope and injecting it into std using using-declarations.
Indeed, in the standard library that my system / g++ 4.8.5 and 6.3.0 use (6.3.0 I checked on coliru), <cstdlib> looks something like this:
// <stdlib.h>:
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
// <cstdlib>
#include <stdlib.h>
namespace std
{
using ::abs;
}
It's that using ::abs which makes std::abs call your function.
You violate the ODR because the GLIBC is a shared library and it also provides an implementation for int abs(int).
That you don't get a "multiple definition of abs(int)" linker error is arguably a bug in the compilers; it would be nice if they warned as about this undefined behavior.
This can be reproduced with this example:
main.cpp
#include <iostream>
int myabs(int);
namespace foo {
int myabs(int n) {
return ::myabs(n);
}
}
int myabs(int n) {
std::cout << "myabs inside main.cpp\n";
return n > 0 ? n : -n;
}
using namespace foo;
int main() {
int k = -1;
std::cout << foo::myabs(k) << std::endl;
}
myabs.cpp
#include <iostream>
int myabs(int n) {
std::cout << "myabs inside myabs.cpp\n";
return n > 0 ? n : -n;
}
Then on the commandline:
g++ -fPIC -c myabs.cpp
g++ -shared myabs.o -o libmyabs.so
g++ -L. main.cpp -lmyabs
Running ./a.out calls the myabs defined inside main.cpp, whereas if you comment out the myabs in main.cpp, it calls the one from myabs.cpp
How to avoid this problem
If you avoid declaring functions in the global namespace, you should mostly avoid this problem.
For your example, if we instead write:
#include <cstdlib>
#include <iostream>
namespace {
int abs(int n) {
return n > 0 ? n : -n;
}
}
using namespace std;
int main() {
int k;
cin >> k;
cout << abs(k) << endl;
}
We get the expected error warning about the call being ambiguous. However, be warned that this doesn't solve the problem if the standard library declares abs in the global namespace:
int main() {
int k;
cin >> k;
cout << ::abs(k) << endl;
}
That seems to just call the standard library version. Naturally, this problem can be avoided by avoiding using namespace std
absfunction, not the one in std. At least in g++ and clangcout << std::abs(k) << endlit still calls the user defined version.cmathcontains essentiallystd::namespace{ using ::abs; using ::acos; using ::asin; /*etc...*/ }.