5
votes

From https://timsong-cpp.github.io/cppwp/basic.compound#3 :

Every value of pointer type is one of the following:

  • a pointer to an object or function (the pointer is said to point to the object or function), or
  • a pointer past the end of an object ([expr.add]), or
  • the null pointer value for that type, or
  • an invalid pointer value.

After using a pointer to explicit call an object's destructor, which of these four kinds of value does the pointer have? Example :

#include <vector>

struct foo {
    std::vector<int> m;
};

int main()
{
    auto f = new foo;
    f->~foo();
    // What is the value of `f` here?
}

I don't believe it can be a pointer to an object or function. There is no longer an object to point to and it is not a function pointer.

I don't believe it can be a pointer past the end of an object. There wasn't any sort of pointer arithmetic and no array is involved.

I don't believe it can be a null pointer value since the pointer is not nullptr. It still points to the storage the object had, you could use it to perform placement new.

I don't believe it can be an invalid pointer value. Invalid pointer values are associated with the end of storage duration, not object lifetime. "A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration". The storage is still valid.

It seems to me like there is no pointer value the pointer could have. Where did I go wrong?

2
Only the first point makes sense IMHO. Still points to an object, although the object is in an invalid state.Macmade
@Macmade It is my understanding that an object ceases to exist after its constructor has completed. Edit : I agree that it is probably the most likely answer, but I am curious as to how it could be.François Andrieux
The value of f doesn't change during, or as a result of, the explicit call of the destructor. Because of the destructor call, f now points at an object that has ceased to exist. Any attempt to use that object (e.g. dereference that pointer) gives undefined behaviour.Peter
@FrançoisAndrieux - The object no longer exists after the destructor call, since the destructor call marks its end of life. The destructor call doesn't change the value of any pointers that (before destruction) held the address of the object.Peter
@FrançoisAndrieux - That pointer is to an object that no longer exists because its lifetime has ended. It is not necessarily an invalid pointer value - it can be compared with nullptr and test non-equal. The standard specifies that using an object outside its lifetime gives undefined behaviour. The standard also says nothing about what the meaning of the value of the pointer is after the object's lifetime has ended - which means, if you attempt to test "validity" of that pointer (beyond comparing it with nullptr) you are in the realms of undefined behaviour.Peter

2 Answers

7
votes

It's the pointer to the object, but the object is just not within its lifetime.

In [basic.compound], footnote 42):

For an object that is not within its lifetime, this is the first byte in memory that it will occupy or used to occupy.

1
votes

It is a pointer that pointed to the object that no longer exists. Of the possible values that the standard lists, only pointer to an object can apply, but only if we consider objects outside of their lifetime to be included in that definition. Invalid could apply if that could include pointers to storage with no objects.

If neither of these can apply, then the list of all values would be defective.


If you were to create a new object into the storage, then this would apply:

[basic.life]

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if the original object is transparently replaceable (see below) by the new object.