5

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?

Igor R.
  • 14,716
  • 2
  • 49
  • 83
  • This is almost certainly a dupe of https://stackoverflow.com/questions/1374037/ambiguous-overload-call-to-absdouble – Andrew Henle Feb 21 '22 at 11:37
  • Since this is from a c standard library I always mindlessly use `std::fabs` for doubles, I didn't know that `` introduced overloaded functions. – MatG Feb 21 '22 at 12:00
  • `C` do not have overloads, so when you call `abs` from global namespace you're calling `C` function with `int` argument. When you using `std::` namespace then you use C++ where overload is available and there is `std::abs(double)`. – Marek R Feb 21 '22 at 12:32
  • https://godbolt.org/z/1jvY6z1vz – Marek R Feb 21 '22 at 12:36
  • @AndrewHenle that's another question. And the answers there state that "the C++ header just includes that and pulls everything into namespace std"... – Igor R. Feb 21 '22 at 14:21

2 Answers2

6

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • So `cstdlib` puts *some* overloads to the global namespace just for C compatibility? – Igor R. Feb 21 '22 at 11:40
  • @IgorR. Yes. I added the relevant paragraph from cppreference. I guess very similar wording can be found in the standard – 463035818_is_not_an_ai Feb 21 '22 at 11:42
  • 1
    @IgorR. More like, `cstdlib` puts some allowed overloads in the global namespace for whatever is convenient for the authors that need to maintain both C and C++ standard libraries. – Passer By Feb 21 '22 at 11:44
4

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).

eerorika
  • 232,697
  • 12
  • 197
  • 326