2

I was trying this code snippet

#include <cstddef>
#include <cstdio>

void f(int* ptr){
    std::printf("int*\n");
}

void f(int val){
    std::printf("int\n");
}

int main() {
    f(NULL);
}

This errors out on both GCC and CLANG but MSVC prints int. As per my reading of [conv.ptr], since a null pointer constant (which is what NULL is) of integral type can be converted to a pointer type, it should be ambiguous for the compiler while selecting the appropriate function since it can bind both to int and int*. I confirmed that all these compilers have NULL implemented as an integral type via

#if defined(_MSC_VER)
    static_assert(std::is_same<decltype(NULL), int>(), "");
#else
    static_assert(std::is_same<decltype(NULL), long>(), "");
#endif

So, is this an MSVC bug or am I missing something and compilers are not expected to throw an error in such cases?

EDIT: I'm aware that nullptr is the way to gauge null-ness, but this question is merely out of curiosity and an attempt at understanding the specification around it.

Zoso
  • 3,273
  • 1
  • 16
  • 27
  • Not sure about gcc and clang, but MSVC has `#define NULL 0`, so it's a simple int type which explains your result. Do you think MSVC does not conform with the standard ? – wohlstad Oct 20 '22 at 12:00
  • 5
    Stop using `NULL` and instead use [nullptr](https://en.cppreference.com/w/cpp/language/nullptr). – Jesper Juhl Oct 20 '22 at 12:01
  • 3
    It all depends on how the implementation defines `NULL` _"...The macro NULL is an __implementation-defined__ null pointer constant, which may be..."_ see https://en.cppreference.com/w/cpp/types/NULL Some implementations will use `0` some will use `std::nullptr_t` And then we have to look at how many implicit conversions are allowed when doing overload resolution. – Richard Critten Oct 20 '22 at 12:06
  • One platform I worked on used `#define NULL __null`, and the `__null` was compiler magic (I think for generating warnings, which had to be opt-in enabled). – Eljay Oct 20 '22 at 12:50
  • 1
    [Related](https://stackoverflow.com/q/8783566), it looks like GCC decided to always ensure `NULL` is defined as `__null` despite this being a non-portable thing. They are allowed to do this as mentioned in comments above. So strictly speaking this failure of resolution is perfectly legal because GCC doesn't actually define a type for `__null` other than "pointer width" as best I can tell. – Mgetz Oct 20 '22 at 13:20

2 Answers2

6

NULL is an old C-compatibility macro. It's usually defined as the plain integer 0. Because of that f(int) will be called.

In C++ you should be using nullptr for null pointers.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

since a null pointer constant (which is what NULL is) of integral type can be converted to a pointer type, it should be ambiguous for the compiler while selecting the appropriate function since it can bind both to int and int*.

It's only ambiguous when the overload resolution has to choose between two equivalent conversion sequences. E.g. from 0L to int or int*.

But overload resolution is trivial when one of the overloads is a perfect match. And as it happens, 0 is a perfect match for int.

Both 0 and 0L have integral type, so all compilers are correct.

Bonus:

void f(std::integral auto val) {
    std::printf("integral\n");
}

Perfect match after template instantiation.

MSalters
  • 173,980
  • 10
  • 155
  • 350