9

In my early days with C++, I seem to recall you could call a member function with a NULL pointer, and check for that in the member function:

class Thing {public: void x();}

void Thing::x()
{ if (this == NULL) return; //nothing to do
   ...do stuff...
}

Thing* p = NULL; //nullptr these days, of course
p->x(); //no crash

Doing this may seem silly, but it was absolutely wonderful when writing recursive functions to traverse data structures, where navigating could easily run into the blind alley of a NULL; navigation functions could do a single check for NULL at the top and then blithely call themselves to try to navigate deeper without littering the code with additional checks.

According to g++ at least, the freedom (if it ever existed) has been revoked. The compiler warns about it, and if compiling optimized, it causes crashes.

Question 1: does the C++ standard (any flavor) disallow a NULL this? Or is g++ just getting in my face?

Question 2. More philosophically, why? 'this' is just another pointer. The glory of pointers is that they can be nullptr, and that's a useful condition.

I know I can get around this by making static functions, passing as first parameter a pointer to the data structure (hellllo Days of C) and then check the pointer. I'm just surprised I'd need to.

Edit: To upvote an answer I'd like to see chapter and verse from the standard on why this is disallowed. Note that my example at NO POINT dereferences NULL. Nothing is virtual here, and p is copied to "argument this" but then checked before use. No defererence occurs! so dereference of NULL can't be used as a claim of UB.

People are making a knee-jerk reaction to *p and assuming it isn't valid if p is NULL. But it is, and the evidence is here: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232 In fact it calls out two cases when a pointer, p, is surprisingly valid as *p: when p is null or when p points one element past the end of an array. What you must never do is USE the value of *p... other than to take the address of it. &*p where p == nullptr for any pointer type p IS valid. It's fine to point out that p->x() is really (*p).x(), but at the end of the day that translates to x(&*p) and that is perfectly well formed and valid. For p=nullptr... it simply becomes x(nullptr).

I think my debate should be with the standards community; in their haste to undercut the concept of a null reference, they left wording unclear. Since no one here has demanded p->x() is UB without trying to demand that it's UB because *p is UB; and because *p is definitely not UB because no aspect of x() uses the referenced value, I'm going to put this down to g++ overreaching on a standard ambiguity. The absolutely identical mechanism using a static function and extra parameter is well defined, so it's not like it stops my refactor effort. Consider the question withdrawn; portable code can't assume this==nullptr will work but there's a portable solution available, so in the end it doesn't matter.

Scott M
  • 684
  • 7
  • 14
  • 4
    `this` cannot be null in the context of the object... – smac89 Jan 02 '18 at 20:23
  • 3
    `this` is a pointer created and defined with every **object** creation that points to that object - i.e. it contains the address of *created* object. Why would it be a `nullptr` if the object was created? What would be the reason to change `this` to point somewhere else? We highly rely on `this` pointing at the right memory address – Fureeish Jan 02 '18 at 20:23
  • 2
    If invoking a method on an invalid object isn't undefined behaviour I'll be stunned. You may have been getting by in the past by the grace of . – user4581301 Jan 02 '18 at 20:24
  • 2
    Those early days (early '80s) were well before the first ISO standard. You used to be able to modify `this`. The source code of CFront even had a check for `this == 0`. You can read more about that part [here](http://www.i-programmer.info/programming/cc/9212-finding-bugs-in-the-first-c-compiler-what-does-bjarne-think.html). – chris Jan 02 '18 at 20:28
  • Related: [What will happen when I call a member function on a NULL object pointer?](https://stackoverflow.com/questions/2533476/what-will-happen-when-i-call-a-member-function-on-a-null-object-pointer) – user4581301 Jan 02 '18 at 20:30
  • 2
    "'this' is just another pointer" I've heard committee members say that if they could go back, they might make it a reference instead. – Marc Glisse Jan 02 '18 at 20:32
  • It is always a good idea to check if the pointer to the object is valid, before calling object's methods. `if (p) p->x();` also produces no crash, and no UB either! – Killzone Kid Jan 02 '18 at 20:33
  • @MarcGlisse I believe, Stroustrup said so. – SergeyA Jan 02 '18 at 21:34
  • "my example at NO POINT dereferences NULL" ... but code contains `p->x()` , you could run for US President – M.M Jan 02 '18 at 22:17
  • @M.M: let's not be insulting. But I stand by the statement. *p does not mean dereference p. It means convert to reference from pointer. I'll add additional backing information to the question. – Scott M Jan 03 '18 at 01:57
  • the word "dereference" means to convert to reference from pointer (or from iterator class) – M.M Jan 03 '18 at 02:38
  • The issue you referred to is still drafting. Maybe in the future indirection through a null pointer will be well-defined, but now, it is UB. – xskxzr Jan 03 '18 at 15:02

5 Answers5

13

To be in a situation where this is nullptr implies you called a non-static member function without using a valid instance such as with a pointer set to nullptr. Since this is forbidden, to obtain a null this you must already be in undefined behavior. In other words, this is never nullptr unless you have undefined behavior. Due to the nature of undefined behavior, you can simplify the statement to simply be "this is never nullptr" since no rule needs to be upheld in the presence of undefined behavior.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • You're right. That answers the question better coming from the UB direction. – user0042 Jan 02 '18 at 20:34
  • 1
    No, that doesn't make sense. p->x() does not dereference p. It passes p as the first (hidden, implicitly named this) argument to x(), which then might proceed to dereference this in various ways, but my example doesn't. There are ways that p->x() could implicitly dereference something. x() could be virtual, and then something like a vtable would get involved. But my example had nothing virtual. To upvote this I need a reference in the standard to undefined behaviour that makes reference to something other than deferencing nullptr, which my example at no point does. – Scott M Jan 02 '18 at 21:58
  • 3
    @user15001 According to **[expr.ref]** in the standard (checked against N4594), `p->x` and `(*(p)).x` must have the same behaviour. The second is clearly dereferencing a null pointer in this case and is bad, so to have the same behaviour the first case must also be bad. – user4581301 Jan 02 '18 at 22:49
  • @user15001 - You're thinking in terms of how a COMPILER might do things. The C++ standard describes meaning of the code construct and, as pointed out by user15001, the meaning in the standard is that `->` does dereferencing. The standard is the yardstick for assessing correctness of implementations (compilers, etc) not the reverse. – Peter Jan 03 '18 at 00:09
  • "The second is clearly dereferencing a null pointer". No. If it was then int* p = &*(int*)nullptr; would crash, but instead it's perfectly legal and executes without issue. * converts a pointer to a reference; it only "dereferences" when context demands it. – Scott M Jan 03 '18 at 00:15
  • @user15001 The standard guarantees `p->x();` is the same as `(*p).x();`. Like it or not, the `*p` in the alternate form performs a dereference and makes the code invalid. More to the point is a nugget linked by Jeremy Friesner's answer drawing attention to **[class.mfct.non-static]** (point 2 in N4594) "If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined." `nullptr` is not a `Thing` or a descendant of `Thing`, therefore undefined behaviour results. The compiler can do whatever it wants. – user4581301 Jan 03 '18 at 00:27
  • @user15001 There is an exception to the dereferencing operator. It's undefined behavior to dereference a pointer that isn't pointing to an object [*"except when the dereference operator is nullified by applying the address-of operator to its result, as in `&*E`"*](http://en.cppreference.com/w/c/language/operator_member_access#Dereference). This exception doesn't cover calling a non-static member function on a `nullptr`. It's not required that you actually use the pointed object's value or state to be undefined behavior. Anything else can be spotted by the compiler and used to optimize. – François Andrieux Jan 03 '18 at 02:05
  • @user15001 The thing with UB is, just because it happens to compile and execute without issue in _your_ particular compiler _today_ doesn't mean it couldn't go horribly wrong tomorrow or when switching to a different compiler. – mindriot Jan 03 '18 at 11:28
  • Hey, I have a macro which is a for loop and used in various location, how to selectively allow null and non-null value to use it . Say the macro is FOR_LOOP(A,N) if( (void*)(A) !=NULL) for(...), If we send A reference of an array It can't be null, if we send A , pointer to an array It can be null, how are we going to selectively stop the warning in this case. – Subramanya Krishna Nov 03 '22 at 07:14
5

Question 1: does the C++ standard (any flavor) disallow a NULL this? Or is g++ just getting in my face?

The C++ standard disallows it -- calling a method on a NULL pointer is officially 'undefined behavior' and you must avoid doing it or you will get bit. In particular, optimizers will assume that the this-pointer is non-NULL when making optimizations, leading to strange/unexpected behaviors at runtime (I know this from experience :))

Question 2. More philosophically, why? 'this' is just another pointer. The glory of pointers is that they can be nullptr, and that's a useful condition.

I'm not sure it matters, really; it's what is specified in the C++ standard, and they probably had their reasons (philosophical or otherwise), but since the standard specifies it, the compilers expect it, therefore as programmers we have to abide by it, or face undefined behavior. (One can imagine an alternate universe where NULL this-pointers are allowed, but we don't live there)

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
0

C++ does not allow calling member functions of null object. Objects need identity and that can not be stored to null pointer. What would happen if member function would read or write a field of a object referenced by null pointer?

It sounds like you could use null object pattern in your code to create wanted result.

Null pointer is recognised a problematic entity in object oriented languages because in most languages it is not a object. This creates a need for code that specifically handles the case something being null. While checking for special null pointer is the norm. There are other approaches. Smalltalk actually has a NullObject which has methods its own methods. As all objects it can also be extended. Go programming language does allow calling struct member functions for something that is nil (which sounds like something required in the question).

Panu
  • 362
  • 3
  • 13
  • C++ allows calling member function in a constructor. The object is not considered initialised until the constructor is completed. – Peter Jan 02 '18 at 23:55
  • That is true. But this is no longer null. I have to find better word for the situation. – Panu Jan 03 '18 at 07:10
0

The question has already been answered - it is undefined behavior to dereference a null pointer, and using *obj or obj-> are both dereferencing.

Now (since I assume you have a question on how to work around this) the solution is to use static function:

class Foo {
    static auto bar_st(Foo* foo) { if (foo) return foo->bar(); }
}

Having said that, I do think that gcc's decision of eliminating all branches for nullptr this was not a wise one. Nobody gained by that, and a lot of people suffered. What's the benefit?

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I suppose one benefit might be that a lot of people learned (albeit the hard way) that the C++ specification doesn't allow you to use null this-pointers... – Jeremy Friesner Jan 02 '18 at 22:58
  • The benefit is for people who took care up front, and ensured they never dereferenced a NULL pointer to call a non-static member function. They don't suffer the performance hit of multiple checks (at least one on every call of a member function) for a situation they have prevented. The only people who "suffer" would be those who write code with undefined behaviour due to bad technique. – Peter Jan 02 '18 at 23:53
  • There are some circumstances where `*p` (if p is a null pointer) is allowed; `p->bar()` is not one of those of course – M.M Jan 03 '18 at 02:42
  • @M.M do you mean unevaluated contexts (such as `decltype(*p)`)? – SergeyA Jan 03 '18 at 15:12
  • @Peter people who would not have used this technique, wouldn't have a branch either, so they did not benefit in any way. – SergeyA Jan 03 '18 at 15:13
  • @SergeyA - you miss my point. I responded to your comment about gcc's decision to eliminate all branches for `nullptr`. If it didn't, the cost - to people who had had ensured they never called a non-static member function through a null pointer - would be unneeded runtime checks inserted by the compiler. Also, in your particular function, a null `foo` still results in undefined behaviour since the function falls off the end. – Peter Jan 04 '18 at 00:33
  • @Peter, no, it is you who miss the point (possibly because you are not aware of what I am talking about). The thing I was referring to is an optimization introduced in gcc about a year ago, when the optimizer eliminated all `if (this == nullptr)` checks from the code. Repeat - the check was coded explicitly by developer, but optimized away. GCC position was that this can never be nullptr in conforming programs, and thus check is not needed. I disagree, see my previous comment as to why. – SergeyA Jan 04 '18 at 14:44
  • @SergeyA - The fact I disagree with your point (and agree with the gcc folks) doesn't mean I missed yours. Coding `this == nullptr` means the developer checks for something that cannot occur without the presence (somewhere else in the program) of undefined behaviour. Removing the checks may be a bit brutal, but does require the developer to address/eliminate actual instances of undefined behaviour rather than ignore them. You may consider that a bad thing, but I don't (such code left unfixed will, more than likely, break when ported to compilers other than gcc anyway). – Peter Jan 04 '18 at 22:17
  • @Peter I can only repeat myself. Removal of the check didn't benefit anybody, and was a purification in sake of purification. I see no reason for it. – SergeyA Jan 05 '18 at 15:34
  • @SergeyA - so you said. I feel like I'm playing chess with a pigeon in this stream of comments, so I'll leave it there. – Peter Jan 06 '18 at 01:31
-3

this might be null too if you delete this (which is possible but not recommended)

  • 2
    'delete this' won't change the value of the this-pointer; rather it will make the this-pointer into a dangling pointer which is unsafe to use. – Jeremy Friesner Jan 02 '18 at 21:36
  • Using the value of a deleted pointer causes implementation-defined behaviour since C++14 (was UB prior to that), so the implementation might define that such a pointer behaves like a null pointer. – M.M Jan 02 '18 at 22:35
  • Stroustrup says: "C++ explicitly allows an implementation of delete to zero out an lvalue operand, and I had hoped that implementations would do that, but that idea doesn't seem to have become popular with implementers." And anyway, `this` is an rvalue. http://www.stroustrup.com/bs_faq2.html#delete-zero – Qwertie Dec 21 '18 at 21:24