5

Let's say I have a C++ function that looks like this:

bool Foo(Bar* b)
{
    if(b == nullptr) {
        return false; 
    }
    // Do stuff
    return true;
}

Let's further say that, because of limitations with some of the tools I have that don't support any standards newer than C++03, I have this code in a header:

#if __cplusplus <= 199711L
    #define nullptr (0)
    #include <stdint.h>
#else
    #include <cstdint>
#endif // End if C++03

My compiler will build this without warning or error. I know I don't have to cast nullptr but is there a compelling reason that I should do it? That is, is there the potential for some bug to creep in relative to to Foo() that using if (b == static_cast<Bar*>(nullptr)) would have avoided and that the compiler wouldn't have warned me about (in either C++03 or C++11 or later)?

My reasons for avoiding the cast would be:

  1. Adding the cast to every use of nullptr seems like it would make the code more cluttered/harder to read.
  2. I haven't seen much (if any) code that does so.

Number 2 isn't a good reason in and of itself, so I'm wondering if it's done because it's more convenient for the code author and/or if it's because there really is no compelling reason to do so.

Edit based on proposed answer: Questions are:

  1. If I'm stuck with C++03 now but still want to plan for C++11 in the future, is the best option to stick with legacy NULL being set to an integer value and all the best-practices that come with it?
  2. If I'm stuck with something of the #define NULL (0) variety, should I always apply a cast? For example: if(b == static_cast<Bar*>(NULL)) is not something I see done very much and I'm wondering if this is out of laziness or if there are good reasons to do or not do this in the case of comparisons (like null pointer checks exemplified above).
  • 1
    You wouldn't need to add the cast to every use of nullptr, you could create a template function, say "CheckIfNull<T>" and do the cast in there. To make the code a little more readable. =) My instinct is to believe the cast would be a good idea, but I'm honestly not sure. I'm interested to find out the answer to this question as well! – Erdrik Ironrose Dec 12 '17 at 16:26
  • @CodingHero: "Number 2 isn't a good reason in and of itself" Yes, it is. What isn't a good idea is using nullptr when you really mean NULL. If you have code that needs to compile on pre-C++11 compilers, then you should universally use NULL. Pretending that nullptr and NULL are the same thing (as you're doing here) is a bad idea. – Nicol Bolas Dec 12 '17 at 16:35
  • I like to treat NULL and nullptr as interchangeable with 0, even though there could be some obscure case where that fails. If it does, I'll just avoid that platform or library or whatever. :-D So I'd just do return b != 0; I get that it's less expressive and more ambiguous than NULL, but dunno, I like plain old zero. Plus I worked in codebases where people treated NULL as zero for integer and boolean-related logic which was retarded, like int x = NULL; which ruins the whole meaning of NULL anyway. – user204677 Dec 12 '17 at 16:52
  • @NicolBolas My reason for saying "Number 2 isn't a good reason in and of itself" was in the vein of doing something "because everyone else is doing it" isn't a good reason, which motivated me to ask for more concrete reasons here. – CodingHero Dec 12 '17 at 16:57
21

Let's further say that, because of limitations with some of the tools I have that don't support any standards newer than C++03, I have this code in a header:

This is a fantastically bad idea.

The expression (0) is (more or less) equivalent to the C++ definition of the NULL. The thing is, nullptr was invented specifically to deal with shortcomings of the NULL.

Therefore, pretending that they are in some way equivalent will only confuse readers. It will make people used to C++11 write code that has different behavior when compiled on C++03 compilers. For example:

void foo(int);
void foo(Bar*);

foo(nullptr);

C++11 tells us that this must call foo(Bar*). But your C++03 macro will instead call foo(int). Since foo(NULL) would cause the same problem, nullptr was invented to stop that.

So having C++03 code that looks like C++11 code but behaves differently is a terrible idea.

nullptr, by design, pretty much never needs to be cast to a specific pointer type. The only exception is if you're calling some kind of function of the form:

template<typename T>
void foo(T* p);

But even then, you can just use foo<ActualType>(nullptr) and get the same effect as foo(static_cast<ActualType*>(nullptr)).

Your not-nullptr needs casting in the same circumstances that NULL does. Indeed, since it is essentially equivalent to NULL, you should just use NULL. And therefore, you have all of the caveats that NULL has.

And FYI: it's pretty much impossible to create a type in C++03 that has identical behavior to C++11's nullptr_t type. You can get close, by using implicit conversions and so forth. But there will always be corner cases where it doesn't work.

So stop promising nullptr behavior that you cannot ensure.

  • Thanks for the reply. I edited original post to make questions obvious. Your post says that the answer to #1 is clearly "yes, use `NULL" but I'm not clear on the answer to #2 or the reasoning behind it. – CodingHero Dec 12 '17 at 17:15
  • 1
    @CodingHero: regarding #2: Casting is only needed if there could be confusion if the null-pointer-constant (NULL, 0, etc.) should be interpreted as an integer or a pointer. When it is identified as being used in a pointer context, the compiler is required to do the right thing. – Bart van Ingen Schenau Dec 13 '17 at 10:52
  • @BartvanIngenSchenau Thanks! That's the input I was looking for. – CodingHero Dec 13 '17 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.