On Thu, 5 May 2022 at 15:33, Sébastien Bini <sebastien.bini@gmail.com> 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.