Date: Tue, 1 Feb 2022 16:39:41 +0100
Hi,
On 01/02/2022 14:21, Maciej Cencora via Std-Proposals wrote:
> Skipping destructor call for moved-from object could be only an
> optimization and not a requirement from standard PoV.
> This way implementers that do not wish to break ABI would keep
> caller-destroy convention, and others would switch to/use
> callee-destroy convention and have better code-gen.
I'm not sure if this is possible. If you have
class IntBuffer {
int *ptr;
IntBuffer() : i(new int[42]) {}
~IntBuffer() { delete[] i; }
};
Then its correct move constructor is
IntBuffer(IntBuffer&& other) noexcept
: ptr(std::exchange(other.ptr, nullptr)) {}
If you have a destroying move constructor / relocation constructor, and
know that `other` is considered destroyed, then you can skip an operation:
IntBuffer(IntBuffer~ other) noexcept
: ptr(other.ptr) {} // correct?
But
1) you can't do that in your relocation constructor if `other` may still
be explicitly destroyed after that constructor call (or you'll delete
the buffer). Your relocation constructor must reset other.ptr, which
means, it just falls back to being the move constructor. So what's the
point of this whole approach?
2) under the same assumption that destruction is a QoI, according to the
paper (but I may have misread something) `other.ptr` in the relocation
constructor is a relocation reference. This implies that
a) you can't even implement the relocation as the move constructor;
you're not allowed to write something into `other.ptr` after building
`ptr`, as `other.ptr` is destroyed (?)
b) if the compiler that built the relocation constructor decided to
destroy `other.ptr`, and then `other` is then destroyed *again*, that's
a double destruction of the `other.ptr` subobject.
My 2 c,
On 01/02/2022 14:21, Maciej Cencora via Std-Proposals wrote:
> Skipping destructor call for moved-from object could be only an
> optimization and not a requirement from standard PoV.
> This way implementers that do not wish to break ABI would keep
> caller-destroy convention, and others would switch to/use
> callee-destroy convention and have better code-gen.
I'm not sure if this is possible. If you have
class IntBuffer {
int *ptr;
IntBuffer() : i(new int[42]) {}
~IntBuffer() { delete[] i; }
};
Then its correct move constructor is
IntBuffer(IntBuffer&& other) noexcept
: ptr(std::exchange(other.ptr, nullptr)) {}
If you have a destroying move constructor / relocation constructor, and
know that `other` is considered destroyed, then you can skip an operation:
IntBuffer(IntBuffer~ other) noexcept
: ptr(other.ptr) {} // correct?
But
1) you can't do that in your relocation constructor if `other` may still
be explicitly destroyed after that constructor call (or you'll delete
the buffer). Your relocation constructor must reset other.ptr, which
means, it just falls back to being the move constructor. So what's the
point of this whole approach?
2) under the same assumption that destruction is a QoI, according to the
paper (but I may have misread something) `other.ptr` in the relocation
constructor is a relocation reference. This implies that
a) you can't even implement the relocation as the move constructor;
you're not allowed to write something into `other.ptr` after building
`ptr`, as `other.ptr` is destroyed (?)
b) if the compiler that built the relocation constructor decided to
destroy `other.ptr`, and then `other` is then destroyed *again*, that's
a double destruction of the `other.ptr` subobject.
My 2 c,
-- Giuseppe D'Angelo | giuseppe.dangelo_at_[hidden] | Senior Software Engineer KDAB (France) S.A.S., a KDAB Group company Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com KDAB - The Qt, C++ and OpenGL Experts
Received on 2022-02-01 15:39:45