7

In C, NULL is normally #defined as either 0 or ((void *)0). I expected this to be similar in C++, and many answers on stack overflow seem to suggest that's the case as well. However, when I try to compile the following program:

#include <stdio.h>

void f(int x) {
    printf("void *\n");
}

void f(void *x) {
    printf("int\n");
}

int main(void) {
    f(0);            // int
    f((void *)0);    // void *
    f(nullptr);      // void *
    f(NULL);         // why is this ambiguous?
    return 0;
}

I'm told by my compiler that f(NULL) is ambiguous. Specifically, my compiler says:

sample.cpp:15:5: error: call to 'f' is ambiguous
    f(NULL);
    ^
sample.cpp:3:6: note: candidate function
void f(void *x) {
     ^
sample.cpp:7:6: note: candidate function
void f(int x) {
     ^
1 error generated.

If NULL was defined as either 0 or ((void *)0), I'd expect it to resolve to the int overload or the void * overload of f, but it doesn't. If NULL was defined to be nullptr for some reason, that would also resolve to the void * overload. What gives?

I compiled on a Mac with g++ --std=c++11 sample.cpp for anyone trying to replicate this. Haven't tried it on other platforms.

For an example of an answer that talks about NULL in C++, see here.

EDIT: I know to always use nullptr over NULL when possible in C++. This question came up when I tried to come up with a few examples of why to show someone.

CoffeeTableEspresso
  • 2,614
  • 1
  • 12
  • 30
  • 2
    compile with `-E` to see what the preprocessor is doing. – Ryan Haining May 21 '19 at 18:38
  • apparently `NULL` is defined as `__null` @RyanHaining – CoffeeTableEspresso May 21 '19 at 18:42
  • 1
    Prefer using `nullptr` over `NULL`, *always*. – Jesper Juhl May 21 '19 at 18:54
  • @JesperJuhl I do whenever I have C++11 or greater, which unfortunately is not often. I was just trying to demonstrate why `NULL` was dangerous and ended up with this mess, which I guess makes a pretty good case for using `nullptr`. – CoffeeTableEspresso May 21 '19 at 18:56
  • I don't know anything more about this, but dug to https://github.com/llvm-mirror/clang/blob/c06fed002f4859ab3eaab5401d7d48a75ffecd89/include/clang-c/Index.h#L2031 – Ryan Haining May 21 '19 at 19:08
  • @CoffeeTableEspresso If you wanna get ahead of all the "use nullptr" comments (or similar on other questions) you're gonna have to call it out really obviously in the question that you already know that. – Ryan Haining May 21 '19 at 19:09
  • 1
    @RyanHaining I've edited, hopefully that's enough lol. This answer seems to cover what `__null` is: https://stackoverflow.com/questions/8783566/where-is-null-defined-in-g – CoffeeTableEspresso May 21 '19 at 20:02

1 Answers1

9

From cppreference since C++11 :

an integer literal with value zero, or a prvalue of type std::nullptr_t

On your platform, it seems if you add a long int overload it resolves the ambiguity. I therefore assume that NULL on your platform with those flags is a long int with the value 0, or 0l : demonstration. I don't know why they chose 0l over just 0, but it is an integer literal with the value zero, so it seems like that's allowed.

This means that the result of this program depends on your compiler and your compilation flags, so beware. The use of nullptr is preferred.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87