I'd like to understand the behavior of the following code, from the C Standard point of view (GCC 9.3, C 20):
#include <cstdlib>
template<class> struct type_tester;
int main() {
type_tester<decltype(abs(0.1))>{}; // int abs(int) overload is selected for some reason!
type_tester<decltype(std::abs(0.1))> {}; // double abs(double) overload is selected, as one would expect
}
So, int abs(int)
is imported to the global namespace, while double abs(double)
is not!
Why?
CodePudding user response:
So, int abs(int) is imported to the global namespace,
Why?
Because the C standard allows it to be imported into the global namespace.
While double abs(double) is not!
Why?
Because the C standard doesn't require it to be imported into the global namespace.
Relevant standard quotes:
[headers]
Except as noted in [library] through [thread] and [depr], the contents of each header cname is the same as that of the corresponding header name.h as specified in the C standard library. In the C standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope of the namespace std. It is unspecified whether these names (including any overloads added in [support] through [thread] and [depr]) are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations ([namespace.udecl]).
Evidently, the C standard library implementation that you use had chosen to use the strategy described in the last paragraph of the quoted rule for the C standard library function, while having chosen not to use that strategy for the C standard library overloads. This specific outcome isn't guaranteed by the standard but it is conforming.
Another possible outcome would be that abs(0.1)
would fail as use of an undeclared identifier. You cannot rely on C standard library names to be declared in the global namespace (unless you use the deprecated <name.h>
C standard headers).
CodePudding user response:
cstdlib
is the C version of the C header stdlib.h
. Such headers must introduce the names in the std
namespace and they are allowed to introcude them in the global namespace. There is no double abs(double)
in the C header, hence there is no reason to introduce it in the global namespace like it is done for the C variants of the funciton. Note that C has no namespaces, and having the function in the global namespace helps for compatibility with C code. For double abs(double)
this is not an issue, because the function does not exist in the C header.
From cppreference:
For some of the C standard library headers of the form xxx.h, the C standard library both includes an identically-named header and another header of the form cxxx (all meaningful cxxx headers are listed above). The intended use of headers of form xxx.h is for interoperability only. It is possible that C source files need to include one of these headers in order to be valid ISO C. Source files that are not intended to also be valid ISO C should not use any of the C headers.
With the exception of complex.h , each xxx.h header included in the C standard library places in the global namespace each name that the corresponding cxxx header would have placed in the std namespace.
These headers are allowed to also declare the same names in the std namespace, and the corresponding cxxx headers are allowed to also declare the same names in the global namespace: including
<cstdlib>
definitely provides std::malloc and may also provide ::malloc. Including<stdlib.h>
definitely provides ::malloc and may also provide std::malloc. This applies even to functions and function overloads that are not part of C standard library.
This applies even to functions and function overloads that are not part of C standard library. That means: It would be allowed to have double abs(double)
in the global namespace too, but it is not required.