Date: Sun, 9 Aug 2020 00:48:27 +0200
On Sat, Aug 08, 2020 at 06:31:50PM +0000, Walt Karas via Std-Proposals wrote:
> Date: Fri, 7 Aug 2020 21:22:45 -0400
> From: Jason McKesson <jmckesson_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] Default assignment operators from
> copy/move constructors
> Message-ID:
> <CANLtd3WeOR0cxt37rs4c-AvuVYWD=q-ZndpFzOrQAECNdVnw=Q_at_[hidden]>
> Content-Type: text/plain; charset="UTF-8"
>
> On Fri, Aug 7, 2020 at 2:47 PM Walt Karas via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > On Friday, August 7, 2020, 1:33:44 PM CDT, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote: On Fri, 7 Aug 2020 at 21:21, Walt Karas via Std-Proposals<std-proposals_at_[hidden]> wrote:
> > >
> > > On Friday, August 7, 2020, 12:57:08 PM CDT, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote:
> > > On Fri, 7 Aug 2020 at 20:47, Walt Karas via Std-Proposals
> > > <std-proposals_at_[hidden]> wrote:
> > > > You can't do a destroy+placement-new if the placement-new can throw.
> > > >
> > > > WK: OK, also good point. If the constructor can throw, then conceptually the default assign can be like this: https://godbolt.org/z/jvdf53
> > >
> > > That approach doesn't work for anything non-trivial, and requires
> > > C++20 implicit object creation otherwise.
> > >
> > > WK: Hmm can you give quick example of a class for which this would not work? Or can you give a smaller reference than two long (and expensive) books?
> >
> >
> > struct X
> > {
> > std::string str;
> > };
> >
> > This type can *NOT* be memcpyed, and restoring it from raw bytes is
> > equally non-doable.
> >
> > WK: it's true that in general it can't be memcpyed. But, in this case we are guaranteed that only one of the copies will be destroyed, so it seems safe. In the generated assign, the raw data of the instance is copied off, and then copied back, to the original instance address before it is destoyed. I can't think of an example of a class for which this would not be safe.
>
> It doesn't matter if a compiler might successfully permit such code to
> "function". The C++ standard declares this code to exhibit undefined
> behavior, and that's unlikely to change anytime soon (not for types
> with non-trivial copies/moves, at any rate). The most you'd ever see
> is some form of destructive-move, and even that is going to, at some
> level, require explicit user intervention to enable for types with
> non-trivial copies/moves.
>
> WK: Compilers don't generate C++ they generate object code. The C++ code I gave is only supposed do describe how the generated object code would work.
>
> This is not Standard-compliant code:
>
> void g(T &);
>
> void f(T &v)
> {
> char cpy[sizeof(T)];
> std::memcpy(cpy, &v, sizeof(T));
> std::memset(&v, 0, sizeof(T));
> std::memcpy(&v, cpy, sizeof(T));
> g(v);
> }
>
> But I can't think of an example of a type T where it wouldn't work. Can you?
I can.
Assume that v contains a lock and that another thread does something with v
that overlaps the memory fiddling. Hilarity ensues.
If you think the single threaded case saves you then consider a signal handler
and a sigatomic_t member.
/MF
> Date: Fri, 7 Aug 2020 21:22:45 -0400
> From: Jason McKesson <jmckesson_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] Default assignment operators from
> copy/move constructors
> Message-ID:
> <CANLtd3WeOR0cxt37rs4c-AvuVYWD=q-ZndpFzOrQAECNdVnw=Q_at_[hidden]>
> Content-Type: text/plain; charset="UTF-8"
>
> On Fri, Aug 7, 2020 at 2:47 PM Walt Karas via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > On Friday, August 7, 2020, 1:33:44 PM CDT, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote: On Fri, 7 Aug 2020 at 21:21, Walt Karas via Std-Proposals<std-proposals_at_[hidden]> wrote:
> > >
> > > On Friday, August 7, 2020, 12:57:08 PM CDT, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote:
> > > On Fri, 7 Aug 2020 at 20:47, Walt Karas via Std-Proposals
> > > <std-proposals_at_[hidden]> wrote:
> > > > You can't do a destroy+placement-new if the placement-new can throw.
> > > >
> > > > WK: OK, also good point. If the constructor can throw, then conceptually the default assign can be like this: https://godbolt.org/z/jvdf53
> > >
> > > That approach doesn't work for anything non-trivial, and requires
> > > C++20 implicit object creation otherwise.
> > >
> > > WK: Hmm can you give quick example of a class for which this would not work? Or can you give a smaller reference than two long (and expensive) books?
> >
> >
> > struct X
> > {
> > std::string str;
> > };
> >
> > This type can *NOT* be memcpyed, and restoring it from raw bytes is
> > equally non-doable.
> >
> > WK: it's true that in general it can't be memcpyed. But, in this case we are guaranteed that only one of the copies will be destroyed, so it seems safe. In the generated assign, the raw data of the instance is copied off, and then copied back, to the original instance address before it is destoyed. I can't think of an example of a class for which this would not be safe.
>
> It doesn't matter if a compiler might successfully permit such code to
> "function". The C++ standard declares this code to exhibit undefined
> behavior, and that's unlikely to change anytime soon (not for types
> with non-trivial copies/moves, at any rate). The most you'd ever see
> is some form of destructive-move, and even that is going to, at some
> level, require explicit user intervention to enable for types with
> non-trivial copies/moves.
>
> WK: Compilers don't generate C++ they generate object code. The C++ code I gave is only supposed do describe how the generated object code would work.
>
> This is not Standard-compliant code:
>
> void g(T &);
>
> void f(T &v)
> {
> char cpy[sizeof(T)];
> std::memcpy(cpy, &v, sizeof(T));
> std::memset(&v, 0, sizeof(T));
> std::memcpy(&v, cpy, sizeof(T));
> g(v);
> }
>
> But I can't think of an example of a type T where it wouldn't work. Can you?
I can.
Assume that v contains a lock and that another thread does something with v
that overlaps the memory fiddling. Hilarity ensues.
If you think the single threaded case saves you then consider a signal handler
and a sigatomic_t member.
/MF
Received on 2020-08-08 17:51:55