Date: Sun, 16 Apr 2023 19:57:10 +0100
On Sun, 2023-04-16 at 21:45 +0300, Andrey Semashev via Std-Discussion
wrote:
> On 4/16/23 21:41, Lénárd Szolnoki via Std-Discussion wrote:
> > On Sun, 2023-04-16 at 21:36 +0300, Andrey Semashev via Std-
> > Discussion
> > wrote:
> > > On 4/16/23 19:38, Jason McKesson via Std-Discussion wrote:
> > > > On Sun, Apr 16, 2023 at 11:36 AM Andrey Semashev
> > > > <andrey.semashev_at_[hidden]> wrote:
> > > > >
> > > > > On April 16, 2023 5:29:27 PM Jason McKesson via Std-
> > > > > Discussion
> > > > > <std-discussion_at_[hidden]> wrote:
> > > > >
> > > > > >
> > > > > > It'd be great if we had some kind of attribute that would
> > > > > > make
> > > > > > it a
> > > > > > warning to use such a type in a non-block scoped way, or
> > > > > > outside of
> > > > > > other block-scoped types. But otherwise, the concept is
> > > > > > valid.
> > > > > > And so
> > > > > > long as that concept is valid, there is no reason to a
> > > > > > priori
> > > > > > forbid
> > > > > > their use as members of other block-scoped types.
> > > > >
> > > > > No, the concept doesn't look valid to me.
> > > >
> > > > I think we may have mixed up the "concept" in question. The
> > > > attribute
> > > > thing was just an off-the-cuff suggestion. "The concept" was
> > > > the
> > > > validity of having block-scoped types being a member of another
> > > > object
> > > > which itself is block-scoped.
> > > >
> > > > > What about having a block-scoped
> > > > > unique_ptr pointing to such type? Presumably, unique_ptr
> > > > > won't be
> > > > > marked as
> > > > > block-scoped, so should this be forbidden?
> > > >
> > > > "Forbidden" is a strong word, but should it be discouraged?
> > > > Yes.
> > > >
> > > > Let's break this down. What we have is a type that is, by
> > > > design,
> > > > intended to be used in a statically block-scoped way. The type
> > > > is
> > > > non-moveable, so users cannot transfer ownership from its
> > > > originating
> > > > named object (prvalues allow them to escape a function call,
> > > > but at
> > > > some point, it gets a name and cannot be moved from there).
> > > > This is
> > > > a
> > > > conceptual invariant: once they get a name, their destructor is
> > > > statically bound to the C++ language scope of that name
> > > > (outside of
> > > > doing oddball things like directly calling the destructor).
> > >
> > > No, scope guards *are* moveable. And I can imagine use cases that
> > > rely
> > > on them being moveable, like returning an epilogue from a
> > > function.
> > >
> > > auto start_transaction(transaction_data& tr)
> > > {
> > > scope_exit completion_guard([&] { tr.complete(); });
> > > tr.init_with_data_data(x, y, z);
> > > return completion_guard;
> > > }
> >
> > Well, you have scope guards, so why not write this?
> >
> > auto start_transaction(transaction_data& tr)
> > {
> > scope_success init_tr([&] {tr.init_with_data_data(x, y, z);});
> > return scope_exit([&] { tr.complete(); });
> > }
> >
> > No need for `scope_exit` to be movable here.
>
> The type needs to be formally moveable, even if the move constructor
> is
> elided, is it not?
>
No, as I'm returning a prvalue. You can return an immovable type since
C++17.
> Anyway, regardless of the example, scope guards are defined as
> moveable
> types.
I think it's probably a mistake for scope_fail and scope_success, as
it's possible to move them across unrelated scopes which breaks the
assumptions of the implementation with uncaught_exceptions().
>
> > BTW, movable or not, `scope_fail` and `scope_success` are broken if
> > you
> > return them from a catch block.
>
> No, because uncaught_exceptions() is already decremented in a catch
> block.
>
You are right, my mistake. Having said that it's possible to move a
scope_fail/success from a scope where uncaught_exceptions() == 1 to a
scope where uncaught_exceptions() == 0 when they are eventually
destroyed. To be fair, it's probably harder to do this accidentally.
wrote:
> On 4/16/23 21:41, Lénárd Szolnoki via Std-Discussion wrote:
> > On Sun, 2023-04-16 at 21:36 +0300, Andrey Semashev via Std-
> > Discussion
> > wrote:
> > > On 4/16/23 19:38, Jason McKesson via Std-Discussion wrote:
> > > > On Sun, Apr 16, 2023 at 11:36 AM Andrey Semashev
> > > > <andrey.semashev_at_[hidden]> wrote:
> > > > >
> > > > > On April 16, 2023 5:29:27 PM Jason McKesson via Std-
> > > > > Discussion
> > > > > <std-discussion_at_[hidden]> wrote:
> > > > >
> > > > > >
> > > > > > It'd be great if we had some kind of attribute that would
> > > > > > make
> > > > > > it a
> > > > > > warning to use such a type in a non-block scoped way, or
> > > > > > outside of
> > > > > > other block-scoped types. But otherwise, the concept is
> > > > > > valid.
> > > > > > And so
> > > > > > long as that concept is valid, there is no reason to a
> > > > > > priori
> > > > > > forbid
> > > > > > their use as members of other block-scoped types.
> > > > >
> > > > > No, the concept doesn't look valid to me.
> > > >
> > > > I think we may have mixed up the "concept" in question. The
> > > > attribute
> > > > thing was just an off-the-cuff suggestion. "The concept" was
> > > > the
> > > > validity of having block-scoped types being a member of another
> > > > object
> > > > which itself is block-scoped.
> > > >
> > > > > What about having a block-scoped
> > > > > unique_ptr pointing to such type? Presumably, unique_ptr
> > > > > won't be
> > > > > marked as
> > > > > block-scoped, so should this be forbidden?
> > > >
> > > > "Forbidden" is a strong word, but should it be discouraged?
> > > > Yes.
> > > >
> > > > Let's break this down. What we have is a type that is, by
> > > > design,
> > > > intended to be used in a statically block-scoped way. The type
> > > > is
> > > > non-moveable, so users cannot transfer ownership from its
> > > > originating
> > > > named object (prvalues allow them to escape a function call,
> > > > but at
> > > > some point, it gets a name and cannot be moved from there).
> > > > This is
> > > > a
> > > > conceptual invariant: once they get a name, their destructor is
> > > > statically bound to the C++ language scope of that name
> > > > (outside of
> > > > doing oddball things like directly calling the destructor).
> > >
> > > No, scope guards *are* moveable. And I can imagine use cases that
> > > rely
> > > on them being moveable, like returning an epilogue from a
> > > function.
> > >
> > > auto start_transaction(transaction_data& tr)
> > > {
> > > scope_exit completion_guard([&] { tr.complete(); });
> > > tr.init_with_data_data(x, y, z);
> > > return completion_guard;
> > > }
> >
> > Well, you have scope guards, so why not write this?
> >
> > auto start_transaction(transaction_data& tr)
> > {
> > scope_success init_tr([&] {tr.init_with_data_data(x, y, z);});
> > return scope_exit([&] { tr.complete(); });
> > }
> >
> > No need for `scope_exit` to be movable here.
>
> The type needs to be formally moveable, even if the move constructor
> is
> elided, is it not?
>
No, as I'm returning a prvalue. You can return an immovable type since
C++17.
> Anyway, regardless of the example, scope guards are defined as
> moveable
> types.
I think it's probably a mistake for scope_fail and scope_success, as
it's possible to move them across unrelated scopes which breaks the
assumptions of the implementation with uncaught_exceptions().
>
> > BTW, movable or not, `scope_fail` and `scope_success` are broken if
> > you
> > return them from a catch block.
>
> No, because uncaught_exceptions() is already decremented in a catch
> block.
>
You are right, my mistake. Having said that it's possible to move a
scope_fail/success from a scope where uncaught_exceptions() == 1 to a
scope where uncaught_exceptions() == 0 when they are eventually
destroyed. To be fair, it's probably harder to do this accidentally.
Received on 2023-04-16 18:57:14