C++ Logo

std-proposals

Advanced search

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

From: Ruud Rietvink <ruud_at_[hidden]>
Date: Sun, 5 Jul 2026 13:19:59 +0200
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.

Received on 2026-07-05 11:20:05