4

The function I want to run:

struct foo;
void bar(const foo* p = 0);

How I call the function:

auto thread = std::thread(&bar, NULL);

The warning:

foobar.h:223:9: warning: passing NULL to non-pointer argument 2 of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (*)(const foo*), _Args = {int}]’ [-Wconversion-null]

What am I missing here?

When I call the function with non-NULL argument, the warning goes away.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • and when you use `0` instead of `NULL`? – wimh Jan 14 '15 at 19:59
  • 6
    You should really be using `nullptr` instead of `NULL` in C++ >= 11. – cdhowie Jan 14 '15 at 20:00
  • In the call? @Wimmel I have changed the prototype, nothing happened. – gsamaras Jan 14 '15 at 20:00
  • I have to look into that @cdhowie, never used it before. – gsamaras Jan 14 '15 at 20:01
  • @cdhowie `nullptr` seems to solve the issue! I did add the pointer after I read that http://stackoverflow.com/questions/13888453/stdthread-unresolved-overloaded-function-type-error. Also, are you going to put an answer with that? If so, you might want to link to this question: http://stackoverflow.com/questions/1282295/what-exactly-is-nullptr – gsamaras Jan 14 '15 at 20:05
  • The `_Args` list for `std::thread` has `, int, int` for the last two parameters. It looks to me like the compiler is confused by the default values or something. – molbdnilo Jan 14 '15 at 20:06
  • Correct @molbdnilo, I noticed that too, but the prototype has pointers as the last two arguments (I also got the same warning for the last argument). It would be nice to know why this happened. – gsamaras Jan 14 '15 at 20:08
  • 1
    @molbdnilo No, `_Args` is deduced from the arguments supplied to the `std::thread` constructor, which implies that whatever the `NULL` macro evaluates to has type `int`. The definition of `NULL` is implementation-defined, so we'd have to know how it's defined in OP's case to understand why it has type `int`. (Typically it's defined as `((void *)0)` in older C code. Obviously it has a different definition here -- probably just `0`, and `0` is implicitly convertible to any pointer type, but *some random `int`*, which is what the `0` becomes as a parameter to `std::thread()`, cannot.) – cdhowie Jan 14 '15 at 20:09
  • @cdhowie Ah, yes, of course. It was too obvious, I guess. – molbdnilo Jan 14 '15 at 20:09
  • @MSalters I said *"in older C code"*. C is a lot more relaxed about conversions between `void *` and other pointer types so it works there. This wouldn't work in C++ because it doesn't allow implicit conversion from `void *` to other pointer types. – cdhowie Jan 14 '15 at 20:12
  • @cdhowie: Are you *really sure* it isn't defined that way in newer C? – Deduplicator Jan 14 '15 at 20:15
  • I got a close vote, why? Should I modify the question for future users? – gsamaras Jan 14 '15 at 20:19
  • @Deduplicator That is one of the possible definitions but is not the only one. – cdhowie Jan 14 '15 at 20:20
  • @G.Samaras: (Not my vote, obviously) The question is somewhat incomplete in that your actual call uses a bunch of undefined variables. Also, the definition of `populate` appears to define a free function unrelated to the member function `&Random_kd_forest::populate`. And finally, you should create a minimal example. This can be reproduced with just `void populate(void*)`. – MSalters Jan 14 '15 at 20:44
  • I see, I will have that in mind next time @MSalters! I am very confused now on which answer I should choose, both seem just fine to me! – gsamaras Jan 14 '15 at 20:46
  • Because of the edit I will choose your answer @Deduplicator, in order to break the tie. :) – gsamaras Jan 14 '15 at 21:10

2 Answers2

5

The trouble is that NULL is a bit ambiguous.
While it is semantically a pointer, it can (and in your implementation is) of integral type.

18.2 Types [support.types]

3 The macro NULL is an implementation-defined C++ null pointer constant in this International Standard (4.10).

4.10 Pointer conversions [conv.ptr]

1 A null pointer constant is an integer literal (2.14.2) with value zero or a prvalue of type std::nullptr_t.
[...]

So your implementation decided to make it plain 0 for backwards-compatibility, but to flag it for extra diagnostics.

Which is actually a laudable decision for promoting portable code.
Though it's a shame noone could hop into a time-machine and just make NULL identical to nullptr in the standard, so the ambiguity does not exist.

To resolve the error, use nullptr instead of NULL or, more involved and not so nice, a pointer of the proper type.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • My only quibble is that the error message is confusing. If your explanation is correct, it should be "Passing integer to pointer arg 2", rather than "passing NULL to non-pointer argument 2". The error message says something opposite to your explanation... –  Jan 14 '15 at 21:18
  • There are 3 cases for `NULL`: 1. It's used in a pointer context. All good. 2. It is used in a non-pointer context and defined as `nullptr_t{}`. A hard error. Cannot happen if the receiving type is deduced, like for a template. 3. It is used in a non-pointer context and defined as `0`. This is the semantic error the warning is about. And it's important that it's `NULL`, because passing a plain integer literal as an `int` argument is somewhat expected. – Deduplicator Jan 14 '15 at 21:28
  • That's what I don't get. The context here is pointer, not non-pointer. What am I missing? –  Jan 14 '15 at 21:36
  • The first sentence in my answer: `NULL` is ambiguous. It is semantically a pointer literal, specifically a null pointer constant. But its type can (and was in this case) an integral type. Which means the template-argument-type is deduced to integral type. – Deduplicator Jan 14 '15 at 21:38
  • After looking some more at this, I now think that "non-pointer context" that error is referring to is ellipses (`Args&&...`) –  Jan 14 '15 at 22:17
2

The problem is that NULL is a macro with value 0. Template argument deduction deduced the type of NULL as int (as you can see from the end of the warning). Yet NULL is by convention used for pointer arguments, not integral arguments. So the compiler warns you that you're passing a NULL argument to an int parameter. To make things worse, this is technically wrong as that non-const integer 0 argument is no longer guaranteed to be convertable to a null pointer.

The correct solution is nullptr. This is, as the name already hints, a pointer instead of an integer.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Since when is `NULL` a macro with value 0, instead of an implementation-defined universal null-pointer constant (which might be plain `0`)? – Deduplicator Jan 14 '15 at 20:16
  • @Deduplicator: Any null pointer constant has _value_ zero, although the type may differ somewhat. – MSalters Jan 14 '15 at 20:22
  • That's simply wrong. My answer has the quotes to proove it. – Deduplicator Jan 14 '15 at 20:29
  • @Deduplicator: You quote the standard which literally states " integer literal with **value zero**", and apparently I'm wrong when I say it's a macro with **value zero** ?? I don't follow your logic. – MSalters Jan 14 '15 at 20:40
  • No, I'm quoting the requirements for `NULL`, which is *null pointer constant*, and the definition of *null pointer constant*, which lists **as one possibility** a zero integral literal. There is that other possibility too. – Deduplicator Jan 14 '15 at 20:43
  • @Deduplicator: One possibility which does not apply to the problem at hand, as we can derive from the warning. I agree that other compilers can indeed choose to use `nullptr`. – MSalters Jan 14 '15 at 20:50
  • Well, if you at least hinted that that part was implementation-defined, instead of cast in stone... especially as the ambiguity of `NULL` was at the heart of the problem. – Deduplicator Jan 14 '15 at 20:53