Safe reuse of allocated storage
Consider the following example:
#include <memory>
struct X
{
X(int &r) : ref(r) {}
int &ref;
};
int m;
int n;
int main()
{
std::allocator<X> a;
X *p = a.allocate(1);
a.construct(p, m);
p->ref = 1; // well-defined
a.destroy(p);
a.construct(p, n);
p->ref = 1; // leads to undefined behavior
a.destroy(p);
a.deallocate(p, 1);
}
This program sequentially creates two objects of type X on the same
memory location. The object of type X created first I will call "the
first object" and the object of type X created second I will call "the
second object".
According to N3225 - 3.8/7:
------------------------------------------
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 storage for the new object exactly overlays the storage location
which the original object occupied, and
- the new object is of the same type as the original object (ignoring
the top-level cv-qualifiers), and
- the type of the original object is not const-qualified, and, if a
class type, does not contain any non-static data member whose type is
const-qualified or a reference type, and
- the original object was a most derived object (1.8) of type T and
the new object is a most derived object of type T (that is, they are
not base class subobjects).
------------------------------------------
According to N3225 - 3.9.2/3:
------------------------------------------
If an object of type T is located at an address A, a pointer of type
cv T* whose value is the address A is said to point to that object,
regardless of how the value was obtained.
------------------------------------------
Thus, p cannot be used to access the member ref of the second object
(probably, a compiler is allowed to assume that in both cases p->ref
refers to the same object, though that's not so). Presumably, the
following approach should not imply undefined behavior:
#include <memory>
struct X
{
X(int &r) : ref(r) {}
int &ref;
};
int m;
int n;
int main()
{
std::allocator<X> a;
void *pv = a.allocate(1);
X *p1 = static_cast<X *>(pv);
a.construct(p1, m);
p1->ref = 1;
a.destroy(p1);
X *p2 = static_cast<X *>(pv);
a.construct(p2, n);
p2->ref = 1; // well-defined now?
a.destroy(p2);
a.deallocate(p2, 1);
}
Here formally pv can never point to the first object, because its type
is not "cv1 pointer to cv2 X". Pointer p2 never points to that object
too. But what may happen if we use single pointer p (as shown below)
instead of p1 and p2?
#include <memory>
struct X
{
X(int &r) : ref(r) {}
int &ref;
};
int m;
int n;
int main()
{
std::allocator<X> a;
void *pv = a.allocate(1);
X *p = static_cast<X *>(pv);
a.construct(p, m);
p->ref = 1;
a.destroy(p);
p = static_cast<X *>(pv); // reassignment
a.construct(p, n);
p->ref = 1; // well-defined?
a.destroy(p);
a.deallocate(p, 1);
}
Does the reassignment with the same pointer value help to avoid
undefined behavior in this case?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]