Date: Sun, 5 Jul 2026 16:21:58 +0200
On 2026-07-05 15:01, Marcin Jaczewski wrote:
> niedz., 5 lip 2026 o 14:11 Ruud Rietvink <ruud_at_[hidden]> napisał(a):
>> 1) I don't suppress base class implementations, like constructors and
>> destructors they are all called.
> But I can suppress member initialization if type is trivial:
> ```
> struct Baz
> {
> int _data[10];
> Baz() // no zero init of `_data`
> {
> }
> Baz(int i) : _data{ i } // all data are set
> {
> }
> };
> ```
+class() has nothing to do with variabele initialization.
>
>> 2) finalizers are independent of destructors, they are called in a
>> different line, before the destructor line.
>> You don't need one to have the other.
>> 3) Exception handling works the same as with destructors, also
>> independent of each other.
> You missed my point, adding this finizers make type not trivial and I
> do not think its good thing.
> At least you would need update every place in standard where they
> refer to not-trivial destructor and add "or finalizer".
>
> Similar case we have with `virtual` , what is the point of `virtual
> -Foo()` when you have normal `~Foo()`?
>
> Overall signature of the finalizer should be the same or at least
> compatible with one from the destructor.
Since finalizing() is separate from destruction(), they may differ in
virtuality.
-Foo() is independent of whatever ~Foo() does. And classes that use + or
- are certainly not trivial.
>> 4) Propogation to member objects? I don't understand what you mean.
>>
> ```
> struct Foo
> {
> Bar _member;
> };
> ```
>
> We have both `-Foo()` and `-Bar()`. When and how `-Bar()` is called?
> Its called right before `~Foo()` or right before `~Bar()`? or called
> by `-Foo()`?
> You can think for case `std::optional<Bar>` too where `Bar` is not a
> direct member of optional.
The same order as with destruction:
First -Foo(), which calls -Bar() at the end, then ~Foo() that calls
~Bar() at the end
>
>> On 2026-07-05 13:56, Marcin Jaczewski wrote:
>>> 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
>>
> niedz., 5 lip 2026 o 14:11 Ruud Rietvink <ruud_at_[hidden]> napisał(a):
>> 1) I don't suppress base class implementations, like constructors and
>> destructors they are all called.
> But I can suppress member initialization if type is trivial:
> ```
> struct Baz
> {
> int _data[10];
> Baz() // no zero init of `_data`
> {
> }
> Baz(int i) : _data{ i } // all data are set
> {
> }
> };
> ```
+class() has nothing to do with variabele initialization.
>
>> 2) finalizers are independent of destructors, they are called in a
>> different line, before the destructor line.
>> You don't need one to have the other.
>> 3) Exception handling works the same as with destructors, also
>> independent of each other.
> You missed my point, adding this finizers make type not trivial and I
> do not think its good thing.
> At least you would need update every place in standard where they
> refer to not-trivial destructor and add "or finalizer".
>
> Similar case we have with `virtual` , what is the point of `virtual
> -Foo()` when you have normal `~Foo()`?
>
> Overall signature of the finalizer should be the same or at least
> compatible with one from the destructor.
Since finalizing() is separate from destruction(), they may differ in
virtuality.
-Foo() is independent of whatever ~Foo() does. And classes that use + or
- are certainly not trivial.
>> 4) Propogation to member objects? I don't understand what you mean.
>>
> ```
> struct Foo
> {
> Bar _member;
> };
> ```
>
> We have both `-Foo()` and `-Bar()`. When and how `-Bar()` is called?
> Its called right before `~Foo()` or right before `~Bar()`? or called
> by `-Foo()`?
> You can think for case `std::optional<Bar>` too where `Bar` is not a
> direct member of optional.
The same order as with destruction:
First -Foo(), which calls -Bar() at the end, then ~Foo() that calls
~Bar() at the end
>
>> On 2026-07-05 13:56, Marcin Jaczewski wrote:
>>> 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 14:22:04
