Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a temporary to pass to rvalue reference

I want to code several recursively interacting merge functions, which I think should have signatures: T&& merge_XYZ(T&& a, T&& b);

They will tend to be used recursively with lines such as:

return merge_XYZ( std::move(x), std::move(y) );

Each of these several merge functions will steal the contents of one of the inputs and inject those contents into the other input and return the result. Typically, they will have x and y which are names for what were rvalue references and thus should be converted back to rvalue references by std::move (correct me if I'm wrong).

But rarely, they have x and or y that are references to objects whose contents must not be stolen. I definitely don't want to write alternate non stealing versions of these functions. Rather, I want the caller to deal with that in these rare cases. So my main question is whether the correct way to do that is to explicitly invoke copy construction, such as:

T temp = merge_QRS( T(x), T(y) ); // use x and y without stealing yet
return merge_XYZ( merge_MNO( std::move(x), std::move(y) ), std::move(temp) );

Main question: Is T(x) the right way to force a temporary copy to be created at that point?

Other questions:
Is T temp = the correct way to make sure the call to merge_QRS in the above code occurs before the call to merge_MNO but otherwise inexpensively forward the temporary from that into the first operand of merge_XYZ? If I used T&& temp instead does it end up holding a pointer to modified T(x) after the life of T(x)?

Is T&& the right return type (as opposed to T) for chaining a lot of these together?

How does the above compare to:

T tx = x;
T&& temp = merge_QRS( std::move(tx), T(y) ); // use x and y without stealing yet
return merge_XYZ( merge_MNO( std::move(x), std::move(y) ), std::move(temp) );

Assuming merge_QRS will be modifying tx and returning an rvalue reference to that, is that behavior all defined?

Writing this question may have helped me realize I could be mixing together two situations that ought not to be mixed: Object you don't want to steal from vs. objects you don't want to steal from yet. Is my original merge_QRS( T(y), T(x)) right (only if consumed within the same expression) for objects I don't want to steal from? But in the case I tried as an example should I have the following:

T tx = x;  // Make copies which can be stolen from
T ty = y;
return merge_XYZ( merge_MNO( std::move(x), std::move(y) ),
                  merge_QRS( std::move(tx), std::move(ty) ) );

I think I may still be confused about stealing the contents vs. stealing the identity. If I return by T&& I'm stealing the identity of one input in addition to stealing the contents of the other. When do I get away with stealing an identity? If I return by T I'm never stealing an identity, and sometimes failing to steal an identity is inefficient.

like image 208
JSF Avatar asked Sep 26 '22 20:09

JSF


1 Answers

Main question: Is T(x) the right way to force a temporary copy to be created at that point?

Yes

Is T temp = the correct way to make sure the call to merge_QRS in the above code occurs before the call to merge_MNO but otherwise inexpensively forward the temporary from that into the first operand of merge_XYZ?

Yes

If I used T&& temp instead does it end up holding a pointer to modified T(x) after the life of T(x)?

Yes. That's dangling reference which unfortunately the compiler won't catch.

Is T&& the right return type (as opposed to T) for chaining a lot of these together?

To be honest, it doesn't smell good to me.

You may want to reconsider your data model to be something more standard, i.e.:

T merge(T x, T y)
{
  // do some merging
  return x;
}

Copy-elision and RVO will eliminate any redundant copies. Now you can move items in, pass copies or pass temporaries. There's only one piece of logic to maintain and your code has value-semantics... which is always better (TM).

like image 173
Richard Hodges Avatar answered Nov 15 '22 09:11

Richard Hodges