C++ Logo

std-proposals

Advanced search

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

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Sun, 5 Jul 2026 16:28:29 +0300
On 5 Jul 2026 14:19, Ruud Rietvink via Std-Proposals wrote:
>
> 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.

In my experience, when I needed explicit initializers or finalizers, I
usually wanted them called at a different time from
construction/destruction, and often with a set of parameters that is
different from constructor. This need typically arises when some of the
data needed to start using the object is not available at the point of
construction. One example is when multiple objects need to interoperate
with each other: you have to construct these objects and then connect
them with each other. Similarly, when you want to shut down such
multi-object system, you may want to disconnect them first.

I see your proposal as too limiting to support this use case. In fact,
it seems your solution is focused on one use case exclusively, namely
calling virtual functions as part of construction/destruction, and
apparently with no way of passing arguments. This seems to me as a too
specialized solution to warrant a core language extension. Especially
since there exist multiple adequate alternative solutions, some of which
you listed.

> 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;

I don't think I find deriving your classes from LogBase a good design.
You would normally use composition for unrelated objects, i.e. you
should probably have a logger member object, which then you would
initialize with logName or whatever using its normal constructor or
public member functions. In this design, you wouldn't need special
initializers as you would just pass logName from your most derived class
down to EngineBase and then the logger in the constructor arguments.

This is not me picking on the particular example you present. This is me
suspecting that, at least in some cases, the problem you to want to
solve comes from suboptimal code design. You may have reasons for such
design, but that still doesn't make it a good design that should be
followed by other programmers or used as a motivating example for a
language change.

Received on 2026-07-05 13:28:34