C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relocation in C++

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Fri, 6 May 2022 01:30:01 +0100
On Thu, 5 May 2022 at 15:33, S├ębastien Bini <sebastien.bini_at_[hidden]>
wrote:

> > Then, the copy/move constructor only needs to be accessible if a function
> > argument is relocated, not if the user is careful to only relocate
> > automatic variables. (It should be accessible even if the ABI is
> > callee-destroy, so that the correctness of code is not
> platform-dependent.)
>
> Isn't it unusual to force different requirements (here on the reloc
> operator) depending on whether the object to relocate is a function
> parameter?
>

Yes, it's unfortunate, but the alternatives (not being able to reloc
function parameters at all, or breaking relocate-only types) are worse.


> Also, If you cannot relocate function parameters, how would you relocate
> an object inside an std::optional? You could still use 'emplace' but this
> more or less forces you to wrap all relocatable types into an optional...
>

You can't relocate function parameters, but it's viable to relocate
*constructor* parameters, since constructors don't have types. So optional
can have an additional constructor taking its prvalue argument by
callee-destroy; this won't require new kinds of function pointer since you
can't form a function pointer to a constructor.

And how do you relocate an object inside a container? Do you need an
> std::vector<T>::push_back(std::optional<T>) API? I'd find that disturbing.
>

You'll need a relocate_wrapper type that can be (possibly implicitly)
constructed from a callee-destroy prvalue, and that releases that value (by
relocation to a prvalue) on request. So you call either
push_back(relocate_wrapper(reloc x)), or relocate_back(reloc x).

Or possibly, relocate_wrapper could be a magic type, favored for prvalue
arguments, much as initializer_list is favored for braced-init-list
arguments. So there's no new ABI, just a new type. (which, uh, could be
baked into the ABI - but that's up to implementors.)

> since this is a totally new interface, you may as well take the argument
> by
> > value. Then also there is no way for user code to call operator reloc in
> a
> > dangerous manner.
>
> I feel mixed about this idea. Developers may find it weird not to have a
> reference somewhere.
>

I think you're right; also it should be possible to take the address of the
source object (for debugging / demonstration purposes if nothing else), so
it will need to be a reference.


> Besides I don't know how we could write base-or-member initializers that
> would call their operator reloc member function (as if by =default) if they
> all take parameters as value, not as reference. This would imply that a
> copy is made somewhere, unless some new language rule applies.
>

Well, I don't think that manually calling member operator reloc should be
possible at all; the only way to reloc a subobject should be by omitting
the corresponding base-or-member-initializer in a user defined reloc
operator, thus allowing the default to occur.

> Then, `= default`
> > performing memberwise relocate indicates that should be the default if a
> > base or member initializer is omitted. Thus there's no need for new
> syntax
> > to indicate that a particular base-or-member is relocated. So your
> example
> > becomes simply:
> >
> > operator reloc(T rhs) {} // equivalent to = default (other than not being
> > trivial)
>
> We do need a new syntax to call this special relocation constructor. The
> main reason is for std::relocate_at implementation (which is needed for
> container implementations). std::relocate_at(const T* src, T* dst) either
> (a) makes memcpy if trivially relocatable, (b) constructs dst with some
> kind of placement-new with the relocation ctor/dtor, or (c) placement-new
> move ctor + dtor: new (dst) T{const_cast<T&&>(*src)}; src->~T();
>

But std::relocate_at is a Standard library function. It doesn't need to be
implementable; you just write language that says what its effects are, the
actual implementation will just call a compiler builtin. Even in the
trivial case you don't want to call memcpy, since you want it usable in
constexpr.

Received on 2022-05-06 00:30:13