C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Idea: moved_from<T>() for Efficient Moved-From State Construction

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Thu, 24 Apr 2025 20:18:52 +0100
On Thu, 24 Apr 2025, 19:27 Andrey Semashev via Std-Proposals, <
std-proposals_at_[hidden]> wrote:

> On 24 Apr 2025 19:45, Jonathan Wakely wrote:
> >
> > In a language where the compiler does not (and in general can not)
> > prevent you from using a moved-from object, it isn't a good idea to
> > make moved-from objects radioactive and unsafe. The reason Jon's
> > argument doesn't make sense for me is because ... this is C++. We
> > don't have a lifetime-checker or borrow-checker that would allow his
> > desired model to work.
> >
> > Frankly, I struggled to find any part of the talk I agreed with.
> >
> > The ViewPort example is just dumb, nothing says he can't implement the
> > cheap move. He offers two bad choices, and misses the obvious good
> > solution. All you need is a ViewPort::valid() member that is false
> > after being moved-from and then say valid() is a precondition for the
> > other operations on it. What's so hard about that?
>
> How would that change the practical state of things, compared to what
> Jon suggests? Ok, you have your valid() function that returns false for
> a moved-from object. You still are not allowed to call any methods on
> the ViewPort except destructor and assignment because now you have
> valid() as a precondition everywhere. So you're back to square one, only
> added a new valid() method that may be never needed by the user or the
> ViewPort implementer.
>
> I understand that the difference is in the formal contract with the
> user, which is more explicitly defined.


Which is an improvement.


But functionally preconditions
> "valid() returns true" and "not in moved-from state" are equivalent.
>

One is testable and can be used to build reliable code (e.g. add assertions
to check preconditions, or check if an object is invalid and allocate new
resources if needed) and one is a radioactive object that you just have to
use correctly and never make mistakes. When the testable version has zero
runtime overhead, what justification is there for the other option?


Neither option is safer than the other as the compiler won't
> automatically test for valid(). That is unless ViewPort implementer adds
> asserts or otherwise checks for the moved-from state everywhere, which
> he could do with or without valid().
>

Users can do the checks for one model, and only the implementation can do
it for the other.


> > Nothing in the standard's "valid but unspecified" model says types
> > can't have preconditions on some operations, e.g. std::unique_ptr is
> > empty after being moved-from, and that means you can't use operator*
> > on it. But that's fine, because being empty is a valid state.
>
> The difference with the standard library is very real. std::list doesn't
> define any equivalent of valid() and also doesn't have a moved-from
> state,


Of course it has a moved-from state. You mean it didn't have a
partially-formed state that could be used as the moved-from state.

which makes its move constructor potentially throwing.
>

No, that's not what makes it potentially throwing. *One particular*
implementation of std::list has a potentially throwing move, but the reason
is backwards compatibility, not because of some inherent limitation of
std::list or any requirement in the standard.

>
The MSVC std::list and libstdc++ std::deque are relics from an older time.
I wish they could be changed, but they can't (but people seem to forget
that their suboptimal moves are still cheaper than copies!).

"Some very rare types with very particular backwards compatibility
constraints are suboptimal" is not a good argument for ... anything. Nobody
is arguing that those types are good designs for how to implement move
semantics. Nobody is saying that's how you should implement your own types,
in fact Thiago has already correctly argued the opposite: the default
constructor should be cheap and non-throwing, and that same state can be
used for the moved-from state.

You don't need partially-formed states, just cheap non-throwing default
constructors.

Pointing to an example of a type that gets that wrong is not a good
argument for a fundamentally more error prone programming model.

Received on 2025-04-24 19:19:09