emacs drives me nuts
emacs drives me nuts

Reputation: 3918

Is "assert (this)" a viable pattern?

Suppose the C++ below. Before calling of a->method1() it has an assert (a) to check if a is sane.

The call a->method2() has no such assertion; instead method2 itself checks for a valid this by means of assert (this).

It that viable code re. the C++ specification?

Even if it's covered by the standard, it not good style of course, and it's error prone if the code ever changes, e.g. if the method is refactored to a virtual method. I am just curios about what the standard has to say, and whether g++ code words by design or just by accident.

The code below works as expected with g++, i.e. the assertion in method2 triggers as intended, because just to call method2 no this pointer is needed.

#include <iostream>
#include <cassert>

struct A
{
    int a;
    A (int a) : a(a) {}

    void method1 ()
    {
        std::cout << a << std::endl;
    }

    void method2 ()
    {
        assert (this);
        std::cout << a << std::endl;
    }
};

void func1 (A *a)
{
    assert (a);
    a->method1();
}

void func2 (A *a)
{
    a->method2();
}

int main ()
{
    func1 (new A (1));
    func2 (new A (2));
    func2 (nullptr);
}

Output

1
2
Assertion failed: this, file main.cpp, line 16

Upvotes: 3

Views: 229

Answers (3)

Jesper Juhl
Jesper Juhl

Reputation: 31475

Answering your question up front: "C++: Is "assert (this)" a viable pattern?" - No.

assert(this); is pointless. The C++ standard guarantees that the this pointer is never nullptr in valid programs.

If your program has undefined behaviour then all bets are, of course, off and this might be nullptr. But an assert is not the correct fix in that case, fixing the UB is.

Upvotes: 3

Asteroids With Wings
Asteroids With Wings

Reputation: 17454

Even if it's [permitted] by the standard

It isn't.


it not good style of course

Nope.


and it's error prone if the code ever changes, e.g. if the method is refactored to a virtual method.

I concede that a virtual member function is more likely to cause a "crash" here, but you already have undefined behaviour and that's not just a theoretical concern: you can expect things like the assertion or conditions to be elided, or other weird things to happen.

This pattern is a big no-no.


I am just curios about what the standard has to say

It says:

[expr.ref/2] [..] For the second option (arrow) the first expression shall be a prvalue having pointer type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2 [..]

[expr.unary.op/1] The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. [..]

Notice that it doesn't explicitly say "the object must exist", but by saying that the expression refers to the object, it implicitly tells us that there must be an object. This sort of "gap" falls directly into the definition of undefined behaviour, by design.


whether g++ code words by design or just by accident.

The last one.

Upvotes: 3

Jarod42
Jarod42

Reputation: 217428

this cannot be nullptr, (else there is already undefined behavior).

in your case

a->method2(); // with a == nullptr

invokes undefined behavior, so checking afterward is useless.

Better signature to mean not null pointer is reference:

void func3(A& a)
{
    a.method1();
}

int main ()
{
    A a1(1); // no new, so no (missing) delete :-)
    A a2(2);

    func1(&a1);
    func2(&a2);
    func2(nullptr); :/
    func3(a1);
}

Upvotes: 2

Related Questions