20

I know the ternary operator has some surprising restrictions, but I was a bit baffled that this fails to compile for me:

void foo(bool b)
{
    int* ptr =  ((b) ? NULL : NULL);
}

Obviously that's the minimum needed to show the problem. The error is:

[BCC32 Error] Unit11.cpp(20): E2034 Cannot convert 'int' to 'int *'

Compiler is the less-than-100%-conforming Embarcadero C++Builder 2010, so a compiler bug is far from impossible...

NOTE: Parens modified to avoid confusion about my intent.

NOTE2: I'd got myself a little confused about how I'd arrived at this construct in the first place, so here's my excuse: I was getting some compilation errors on a line like a = b? c : d, where b, c and d were all complex expressions. To narrow it down, I replaced c and d with NULLs in order to check if b was the culprit. At this point, everything went to hell in a handcart.

Roddy
  • 63,052
  • 38
  • 156
  • 264
  • 1
    GCC is fine with it: http://www.ideone.com/ddUyR. :( – R. Martinho Fernandes Jul 25 '11 at 16:58
  • 2
    +1 for a minimally complete example program. – Robᵩ Jul 25 '11 at 17:08
  • If you're compiler supported C++0x you could use `nullptr` however `NULL` is really a macro defined as a literal integer in most implementations. i.e `#define NULL 0` – AJG85 Jul 25 '11 at 23:36
  • Embarcadero appreciate bug reports. You can log it here: http://qc.embarcadero.com/wc/qcmain.aspx – David Jul 26 '11 at 02:58
  • @David M: Thanks, I wanted to get consensus here before QC'ing it. And consenus is it's not a bug! – Roddy Jul 26 '11 at 09:18
  • @AJG85 : Encouragingly, the IDE highlights `nullptr` as a keyword, but sadly the compiler barfs. BCB2010 has some C++0x goodness, but not all. – Roddy Jul 26 '11 at 09:21
  • @Roddy I don't think any compiler supports it all yet as it isn't finalized ... I sorely wish VS2010 had variadic templates. – AJG85 Jul 26 '11 at 15:50

3 Answers3

23

NULL is a macro that expands to 0 (or some integral constant expression with a value of 0, for example, (1 - 1)). It's not otherwise "special."

Any integral constant expression with a value of zero is usable as a null pointer constant, which is the reason that int* ptr = 0; is permitted. However, here, the expression is b ? 0 : 0; this is not an integral constant expression (b is not constant); its type is int, which is not implicitly convertible to int*

The workaround would be to explicitly specify that you want a pointer type:

int* const null_int_ptr = 0;
int* ptr = b ? null_int_ptr : null_int_ptr;

The example is a bit contrived, though: usually when one uses the conditional operator, at least one of the arguments is actually a pointer type (e.g. b ? ptr : 0); when one of the operands is a pointer type, the 0 is implicitly converted to that same pointer type and thus the type of the entire conditional expression is the pointer type, not int.

The only case where you can have this "problem" is where a null pointer constant is used as both the second and third operands of the conditional operator, which is rather odd.

James McNellis
  • 327,682
  • 71
  • 882
  • 954
  • 7
    What about `nullptr` from C++11? Would `int* ptr = b ? nullptr : nullptr;` work? – R. Martinho Fernandes Jul 25 '11 at 17:01
  • @Martinho: Perhaps; I have not yet had the opportunity to use `nullptr`. – James McNellis Jul 25 '11 at 17:04
  • Actually, that compiles in gcc for some reason. Checked after I wrote my answer :) – unkulunkulu Jul 25 '11 at 17:06
  • 4
    Yes, `nullptr` would compile just fine, as the result type will be `nullptr_t`. This kind of thing is why `nullptr` was added to the language. – Puppy Jul 25 '11 at 17:09
  • @James - Not sure how contrived. I found this problem with a an `a= b?NULL:c` expression, and then just tried the NULL:NULL to try and reduce the problem further. ... Hmm. I need to re-try some tests here :-( – Roddy Jul 25 '11 at 17:12
  • @Roddy: Well, `c` in that case is either (a) a null pointer constant or (b) an actual pointer. If it's (a) then it doesn't make much sense because the statement is effectively `int* ptr = 0;`; if it's (b), then you should not get this error. – James McNellis Jul 25 '11 at 17:13
  • I think it surprises people that the conditional operator result can be a different type than its parameters; one of my coworkers was convinced there was a C++ bug because of this. – Mark Ransom Jul 25 '11 at 17:25
  • @James, I worked out how I'd got to the `NULL:NULL` construct - see above. `NULL:x` works fine. Thanks. – Roddy Jul 25 '11 at 17:27
4

Your problem is that on your system NULL is defined to be 0 which is assumed to be an int in the context of the ternary operator. If you static_cast one of the operands to int* it should auto-promote the other one.

But why are using such a construct in the first place?

Mark B
  • 91,641
  • 10
  • 102
  • 179
  • I was using something like `a = b ? NULL : c;` The `NULL:NULL` just removed an extra piece of context and seemed even more surprising to me. – Roddy Jul 25 '11 at 17:06
  • Hmmm. maybe I need to go back a step or two in my tests. Watch this space... And thanks. – Roddy Jul 25 '11 at 17:16
3

NULL could be defined as having type int or even long, so the ternary operator has the same type. There's no implicit conversion to the pointer type, so compiler generates an error. The gotcha here is that there is an implicit conversion from constant integer expression evaluating to zero (the infamous null pointer constant).

Possible solution here is an explicit cast:

int* ptr =  b ? (int*) NULL : NULL;
unkulunkulu
  • 10,526
  • 2
  • 27
  • 47