C++ Logo

std-proposals

Advanced search

Re: [std-proposals] c++ proposal for Initializer/finalizer, version 0.1

From: Sebastian Wittmeier <wittmeier_at_[hidden]>
Date: Sun, 5 Jul 2026 16:37:39 +0200
Your proposal combines two features (and it could make sense to combine them), but I want to separate them in this post to see their individual usefulness and composability:   1. You introduce functions, which are automatically called one after the other for the inheritance hierarchy. The + function is called from Base to most Derived. The - function is called from most Derived to Base. That happens with the constructors and destructors, but not for normal member functions. Only if they explicitly call each other. That feature / language extension itself can be useful.   2. You introduce functions to be called after construction / before destruction. That reminds of the Win32 Events: WM_CLOSE vs. WM_DESTROY. The first is called before closing of a window (and it can even abort), the second after closing of a window. It can make sense that a (possibly abstract) base class can access a functioning object in its constructor or destructor.   -----Ursprüngliche Nachricht----- Von:Ruud Rietvink via Std-Proposals <std-proposals_at_[hidden]> Gesendet:So 05.07.2026 13:20 Betreff:Re: [std-proposals] c++ proposal for Initializer/finalizer, version 0.1 An:std-proposals_at_[hidden]; CC:Ruud Rietvink <ruud_at_[hidden]>; C++ object initializer and finalizer ==================================== Date:    2026-07-05 Version: 0.1 Author:  Ruud Rietvink Problem ------- In the constructor and destructor of an object you cannot use virtual methods properly. So calling a virtual method in a base class to get some possible derived value is not possible. During construction and destruction there is no guaranteed time that the object is fully constructed, so that virtuality covers all the layers of the object. Possible (partial) solutions ---------------------------- 1) One solution is to call an initialize() method after construction, and a finalize() method directly before destruction. This is tedious and can easily be forgotten (exceptions, end of scopes). 2) The initialize() could be run automatically in a factory, calling initialize() in the factory create() method. You would have to force that the construction only takes place through this factory by a private/friend construction, or by adding a special factory tag to the constructor arguments. This doesn't solve the finalize() method though, nor copy construction/cloning. 3) With a RAII wrapper, you could call the initialize() method in the RAII constructor and the finalize() in the RAII destructor. Together with a special constructor tag you could force the RAII constructor to be used. This can be made generic. This does require more memory (pointer in RAII object). This fails for copy construction/cloning. 4) This could be done with templated inheritance. template <typename T> class LogWrapper : public T { public:    template <typename... args>    LogWrapper(T&&... args) : T(std::forward<Args>(args)...)    {       initializeLog();    }    ~LogWrapper()    {       finalizeLog();    } } Wrapping could be nested for different purposes (LogWrapper<XXXWrapper<Class>> object(....)). This fails when the class has clone() functionality (unless said API uses some standard copyable in the Wrapper). X) For all wrapper solutions the problem is that when you want to start or stop using it, or extend it, it is a lot of coding effort. Proposal -------- Proposal is to introduce an initializer method that is automatically called after the construction is complete, and a finalizer method that is automatically called before the destruction of an object. Because the finalizer is called before the destructing, the vtable is still correct and virtual calling will work correctly. Proposed names:    class::+class() for initializing    class::-class() for finalizing Initializer is also called after copy construction. Example:    class LogBase    {    protected:        virtual ~LogBase() = default;        ...        virtual const char* const logName() = 0; // or c++26-reflection        +LogBase()         { logStart(logName()); }        virtual -LogBase() { logEnd(logName()); }        ...    };    class EngineBase: protected LogBase    {    protected:        ~EngineBase() override = default;        ...        virtual EngineType engineType() = 0;        +EngineBase()          { addToEngine(engineType()); }   // Optional, otherwise default        -EngineBase() override { removeFromEngine } // Optional        ...    };    class Derived: protected EngineBase    {    protected:        ...        EngineType engineType()     override { return EngineType.Something; }        const char* const logName() override { return "Derived Something"; }        ...    };    EngineBase* base = new Derived();    ...    delete base;    // Order:    // LogBase()    // EngineBase()    // Derived()    // Object intact.    // +LogBase()     (same order as constructor chain)    // +EngineBase()    // +Derived()     (if any)    // Life...    // -Derived()  (if any, same order as destructor chain, for all implementations of -class())    // -EngineBase()    // -LogBase()    // Object still intact.    // ~Derived()    // ~EngineBase()    // ~LogBase()  Advantages  ----------  a) Easy to implement for compilers.  b) Easy to optimize away for compilers, if not needed.  c) No clashing new keywords.  d) Completely optional and backwards compatible.  e) If not used, not in the way.  f) Could start a new paradigm: construct in (), initialize in +(), finalize in -(), destruct in ~().     Also a lot of construction is moved to header file, these days.     Also substitutes often used init() method when multiple constructors are present. -- Std-Proposals mailing list Std-Proposals_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-07-05 14:41:54