C++ Logo

std-proposals

Advanced search

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

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Sun, 5 Jul 2026 13:56:01 +0200
I had somehow similar idea for "finalizers" but its was more "unwind" operators
that are called before dstructors is called.

Main difference would be that different types of stack "unwinds" would
have its own functions
to make difference between exception unwinding and normal scope exit.
Or even cancellation of coroutines. Something would be useful for db
transact scope cancellation.


One thing I realized in my solution is that even in your case this new
`+foo()` and `-foo()` operators
need to have the same exception specification and triviality as the
constructor or destructor.
E.g. adding `-foo() noexcept(false) { throw 1; }` would change the
result of `noexcept(foo{})`.
This should be a hard error if the destructor does not have `noexcept(false)`.
This is needed to avoid unexpectedly changing behavior of other code
than need to know this things.

Another thing to consider is how you control base functions of this
initializers and finishers functions?
How to suppress base class implementation?
How to propagate it to member objects? And who and when do that?
Is `virtual ` required on finalizers when you have a `virtual` destructor?

niedz., 5 lip 2026 o 13:20 Ruud Rietvink via Std-Proposals
<std-proposals_at_[hidden]> napisaƂ(a):
>
>
> 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 11:56:16