Search code examples
c++memory-managementlanguage-lawyernew-operator

NoFieldsClass::operator new(std::size_t): does the standard allow for it to return the same address each time it's invoked?


As part of an optimization strategy, I would like to "pretend" I'm actually allocating an object on the heap, when in fact I would just reuse a pre-existing object, without the rest of the application ever noticing because no check is performed on the returned address and no fields from the objects will ever have to be accessed. Adding to that, the class constructor has no side effects.

I would therefore like to know whether the following program exhibits a behavior which is well defined, implementation-specific, undefined or invalid altogether, by the word of the standard.

#include <memory>
#include <cassert>

class Base {
public:
    Base(){}
    virtual ~Base(){}
};

class Derived: public Base {
public:
    using Base::Base;

    static void *operator new(std::size_t s)  {
        static void *mem = ::operator new(s);

        return mem;
    }

    static void operator delete(void *mem) {
        /* no-op */
    }
};

int main() {
    using Ptr = std::unique_ptr<Base>;

    Ptr p1 { new Derived };
    Ptr p2 { new Derived };
    Ptr p3 { new Derived };
    Ptr p4 { new Derived };

    // assert just in this test, not in the real program
    assert(p1 == p2 && p2 == p3 && p3 == p4);

    return 0;
}

Reading §6.7.2.9 of the current C++ working draft (N4835) it seems it's invalid:

Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage ²⁹.

However, the note 29 which the above paragraph refers to, states that:

Under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference.

As stated at the beginning, in my case the program couldn't care less about the address at which this object is allocated, all that is required is that it can be allocated with operator new and gotten rid of with operator delete, so it seems to fit within the requirements made by note 29. Is this correct?


Solution

  • [basic.stc.dynamic.allocation]/2 The allocation function attempts to allocate the requested amount of storage... If the request succeeds, the value returned shall be a non-null pointer value (7.11) p0 different from any previously returned value p1, unless that value p1 was subsequently passed to an operator delete.

    Emphasis mine.