Suppose we were to have a particle system. We want to implement this as a pool of particles. This means that we are going to want to recycle the same particles over and over again, so to reuse resources, we will want to pass around rvalues.
Now, suppose that our particles have a very short life time, but we want something to happen when they "expire" (like incrementing an integer x
) but we still want to recycle them. Now, suppose that we want to be able to specify x
. But, now what do we do?
On move, we want to be able to call a function to increment a variable, but that variable must fluctuate. We wouldn't want to put this into a destructor because this would involve either templates to derive the exact function call at compile time or we would need a std::function
or function pointer to refer to the function from inside a particle, wasting space. What we want to do is to be able to take in an expiring value, do an action, and then forward it. In other words, we want an outgoing-move with a side effect specifically on the conversion from lvalue to rvalue.
It does matter on which action you do it--on lvalue to rvalue conversion or on receiving an into another Particle with operator=
or operator()
. Either you do it when an object receives a value or when you take the rvalue in. But suppose we want to do this for many different types of objects--do you want to write 5 or more different move functions for each of 5 different classes, or perhaps should we parameterize over the types with a external templated function?
How else would you do this? You're still going to use std::move()
from inside the object, but we want to couple it with a function external to the object, because this would represent a side effect with a move.
Coliru: http://coliru.stacked-crooked.com/a/0ff11890c16f1621
#include <iostream>
#include <string>
struct Particle {
int i;
};
template <typename T>
T&& move( T& obj, int (*fn)(int) , int& i ) {
i = fn(i);
return(std::move(obj));
}
int increment(int i) { return i+1; }
int main() {
// Have some object pool we want to optimize by moving instead of creating new ones.
// We'll use rvalue semantics so we can "inherit" lifetime instead of copying.
Particle pool[2000];
// Fill up the "particles".
for (auto i = 0; i < 2000; ++i) {
pool[i].i = i;
}
// Perform the moves with side effects.
int j = 0;
for (auto i = 0; i < 1999; ++i) {
pool[i+1] = move<Particle>(pool[i], &increment, j);
std::cout << "Moves performed: " << j << '\n';
}
}