0

Say we have a struct:

struct S{ int _i = 1; };

I would like to write a function which updates an instance of S, like this:

void update(S& s) { s._i += 1; }

I usually prefer to return values rather than passing an argument by reference and changing it. Something like this:

S update(S s)
{
    S ret = std::move(s);
    ret._i += 1;
    return ret;
}

I would then be able to call this last function like this (in case I want a copy of the previous and updated instance):

S s{};
S u = update(s);

Or like this (in case I'm only interested in keeping the updated value):

S s{};
s = update(std::move(s)); // is this legal?

So, I move s into the argument of the update function, and retrieve it back at the assignment (last line). Is this legal? Apart from that, is this just silly and should I go with the first solution?

cigien
  • 57,834
  • 11
  • 73
  • 112
mfnx
  • 2,894
  • 1
  • 12
  • 28
  • In the second `update` version, `s` is already a local variable, a copy of the argument. There's little point to move it to yet another local variable. You can just as well do `++s._i; return s;` – Igor Tandetnik May 15 '21 at 23:44
  • Yes, `s = update(std::move(s));` is legal. For `S` as shown, it's pointless since move is equivalent to copy. – Igor Tandetnik May 15 '21 at 23:47
  • You could create the member function `S& operator++() { ++_i; return *this; }` instead. That would give your `class` a more familiar interface. – Ted Lyngmo May 15 '21 at 23:49
  • @TedLyngmo The thing is, in my real code, S does not know how to update itself. It's done in another class. – mfnx May 15 '21 at 23:59
  • Ok. That sounds a little odd. – Ted Lyngmo May 16 '21 at 00:00
  • @IgorTandetnik How is move equivalent to copy? I would like to avoid copying, similar to the first version of update. – mfnx May 16 '21 at 00:00
  • @TedLyngmo If I pass S to the update function of one class, it gets updated in one way. If I pass it to the update function of another class, it gets updated in another way. – mfnx May 16 '21 at 00:08
  • For a simple object like `int`, there's no difference between move and copy. The same is true of a struct that consists only of such objects, like your `S`. Perhaps your real `S` is more elaborate, containing members for which move is in fact more efficient than copy. – Igor Tandetnik May 16 '21 at 00:16
  • @IgorTandetnik Ok, I understand. S in my case is more elaborate, but no heap memory is used. Taking that into account, would the second version be as efficient as the first version (passing by reference)? – mfnx May 16 '21 at 00:18
  • The first version of `update` definitely doesn't involve any copying (of objects larger than a pointer). The second version most likely needs to copy at least `sizeof(S)` bytes. It's possible that a sufficiently aggressive optimizer would be able to see through `s = update(std::move(s))` and optimize all the copying away (assuming `update` is inline, its implementation visible at the call site). – Igor Tandetnik May 16 '21 at 00:24
  • Realize that `std::move(s)` doesn't move anything - it's simply a convenient way to write `static_cast(s)`, which essentially means "I don't care what state `s` is in afterwards" and gives permission to the compiler to scavenge it for parts. It appears that in your case, `S` doesn't actually contain any parts that can be scavenged (detached from `s` and transferred to another `S` instance), so moving is once again no different than copying. The only question is whether a smart optimizer could see though all these calls, avoid copying and perform the update in place. – Igor Tandetnik May 16 '21 at 00:29
  • @IgorTandetnik Ok, I'll try to investigate that. I guess the first version of update might be the better one. – mfnx May 16 '21 at 00:34

0 Answers0