I have read this, this, this and other similar topics. They all show examples of how we can roll back simple changes made to an object in case of an exception to preserve the original state of the object. Yes, if we just push a new element to a vector, it is not hard to pop it back if something goes wrong. But what if we perform more complicated operations on multiple objects than just pushing new elements, and we need to preserve the original state of each object if an exception occures?
// Checkpoint
vec1.erase(
std::remove_if(vec1.begin(), vec1.end(), [](/*...*/) { /*...*/ }),
vec1.end());
std::sort(vec2.begin(), vec2.end(), [](/*...*/) { /*...*/ });
std::stable_partition(vec3.begin(), vec3.end(), [](/*...*/) { /*...*/ });
In the above example, if, say, the last stable_partition()
function throws std::bad_alloc
, this is not so trivial to undo the previous operations made on other containers. We cannot just "unsort" or "unerase" a container as easy as calling pop_back()
. How can we gracefully roll back to the checkpoint? Yes, we can make a local copy of each container, perform necessary operations on these copies and then, if everything is ok, we can use noexcept swap. But this approach implies a lot of overhead, especially if the objects are expensive to copy. Can we do something better?
And how about this?
object1.method(); // Changes the internal state of object1
object2.method(); // Changes the internal state of object2
object3.method(); // Throws
If the method of object3
throws, how can we undo the methods of object2
and object1
? How do you deal with such cases?