C++ Logo


Advanced search

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

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Wed, 1 Jun 2022 17:53:15 +0200
> Another point: std::relocate_at(p, q) is just new (p) T(std::relocate(q)).
> So if anything it's std::relocate_at that's unnecessary.
> I agree with you on the confusion; since we expect non-experts to use
> operator reloc only, std::relocate could be given a longer, more
> technically oriented name.

I agree with both statements.

> As a tiebreaker, prefer passing by value for prvalue arguments.
> Specifically, I would amend [over.ics.rank]/3.2.3 to read:
> - neither of S1 and S2 bind a reference to an implicit object parameter of
> a non-static member function declared without a ref-qualifier, and either:
> - S1 binds an lvalue reference to an lvalue, and S2 does not, or:
> - S1 binds an rvalue reference to an xvalue, and S2 does not, or:
> - S1 does not bind a reference, and S2 binds a reference to a prvalue,
> or:
> - S1 binds an rvalue reference to a prvalue, and S2 binds an lvalue
> reference [Example:
> int i;
> int f1();
> int&& f2();
> ...
> int g2(const int&);
> int g2(int);
> int g2(int&&);
> int j2 = g2(i); // calls g2(const int&)
> int k2 = g2(f1()); // calls g2(int)
> int l2 = g2(f2()); // calls g2(int&&)
> ...
> - end example]

That sounds nice and shouldn't break existing APIs as they would today be
considered as ambiguous.

> The user can write destroy+relocate if they want; the language shouldn't
> be doing that.

As a user I would feel uncomfortable explicitly calling the destructor on
*this to reconstruct it again.

> This does require a bit of a conceptual leap, yes. The key is to
> understand that operator reloc *changes the value category* of a named
> object, from lvalue to prvalue. This is because relocation is the inverse
> operation to prvalue materialization. So by the time the relocating
> constructor is invoked, the argument is already a prvalue (just, uh, a
> prvalue that occupies storage).

Just to be sure I understand correctly: the reloc operator would no longer
return a newly built instance (i.e. a temporary), but instead would merely
transform the value category from lvalue to prvalue?
And then whether a temporary is materialized or not depends on the context,

Consider a type T with that putative relocation constructor T(T):

Case 1:
void foo(T a_foo);

T var;
foo(reloc var);

In `foo(reloc var);` is a_foo constructed using T(T)?

If so then `var`'s destruction is delegated to T(T), which prevents the
destructor call of its parameter, right?

Case 2:
Now let's add a T& operator=(T):

T objA, objB;
objA = reloc objB;

Following the same logic as above, objB is passed to relocation
constructor, and then to objA's assignment operator, right?

Case 3:
T obj;
reloc obj; // premature end-of-scope

This turns obj into a prvalue that is captured nowhere. I guess in this
case the destructor of obj is simply called?

Case 4:
void bar(T&& bar_param);

T var;
bar(reloc var);

Again, reloc var turns var into a prvalue, but bar cannot capture it. So a
temporary object of type T is created and its address is passed to bar,

If I understand correctly, then this could simplify some aspects of how
operator reloc works.

However I fear many users will be lost (most C++ programmers that I know
have no knowledge of value categories), and will find it quite disturbing
to have a relocation constructor with that signature T(T). Sure operator
reloc(T&&) may be confusing as well, but my personal take is that it would
be less so :/

Received on 2022-06-01 15:53:28