Reader Q&A: How can I prevent a type from being instantiated on the stack?

Anubhav asked:

An interesting question has come up in our project while debating operator new as a class member function. Specifically, the question is about whether such a class should be allowed to be instantiated on stack. The understanding is that a class providing its own operator new would likely have special layout considerations which would not be met if the object of such a class is instantiated locally.

The class specific operator new is used when allocating on the heap, but objects can still be allocated on the stack. The only way I know to prevent an object from being instantiated on the stack is by forcing creation through a factory function that always allocates on the heap, for example:

#include <memory>

class X {
// make all ctors private
X();
X(const X&);
// and any other ctors go here

public:
// Factory function (or it could be a nonmember friend)
static auto make( /*…*/ ) { return std::unique_ptr<X>(new X( /*…*/ )); }

// the usual public member functions go here
};

int main()
{
X x; // error
auto x2 = X::make(); // ok
}

9 thoughts on “Reader Q&A: How can I prevent a type from being instantiated on the stack?

  1. But std::make_unique also requires access to the constructor, right? X::make could create the unique_ptr manually instead.

  2. @mttpd: The protected destructor solution also works, but it’s a C++98 idiom that’s now inferior because it requires using a custom ‘deleter’ function that is possible, but less natural and idiomatic in modern C++, to use together with unique_ptr and shared_ptr.

    @Petter: Good point, thanks. Fixed.

  3. @Good point, thanks. Fixed.

    But this also gives up on the perf and memory layout advantages of std::make_shared… In library code this could be premature pessimisation. What about:

    class X
    {
    //make all ctors private

    struct PrivateConstructionTag {};

    public:
    static auto make( /*…*/ ) { return std::make_shared( /*…*/ PrivateConstructionTag()); }
    };

    I’m using this pattern in my library code. Is this the best solution?

  4. Correction:

    	X(..., PrivateConstructionTag);
    public:
    

    should be

    public:
    	X(..., PrivateConstructionTag);
    
  5. Hi. Does std::make_unique have the same problems with the private constructor pattern as std::make_sharedcode> have? I.e. that it is not portably possible to make std::make_shared a friend of X in the example. What is the preferred way to solve this problem? Is the use of the PrivateConstructorTag the only way?

  6. If

    make_shared()

    is the plan anyway, then deriving from

    std::enable_shared_from_this<>

    may be adequate. No?

  7. @Grout: Deriving from enable_shared_from_this doesn’t actually block compilation of code that instantiates the object on the stack. Nor does it block compilation of new Foo(). I have found the PrivateConstructorTag to be the best way to ensure that a class that inherits from enable_shared_from_this is only constructed with make_shared().

  8. My idea is a wrapper around. Is there any issues?

    template <typename Wrapped> class HeapOnly 
    {
    public:
    	typedef std::unique_ptr<Wrapped> TPtr;
    	typedef const std::unique_ptr<Wrapped> TConstPtr;
    	
    private:
        TPtr m_wrapped;
    
    public:
    
        operator TPtr&      ()       { return m_wrapped; }
        operator TConstPtr& () const { return m_wrapped; }
    
        HeapOnly(/* sorry, but I'm unfamiliar with variable templates so far...*/) 
            : m_wrapped( std::make_unique<Wrapped>(/*...*/) )
        {}
    };
    
    class A {
    private:
        A() {} // private ctors
    
    	// ...
    public:
        // ... others 
        friend class HeapOnly<A>;
    };
    
    void f()
    {
         HeapOnly<A> compilationOk;
         A compilationError;
    }
    

Comments are closed.