C++ Basics: Restore State with a Small Scope Guard
Use a noncopyable destructor-based guard to restore a flag even when an exception leaves the scope.
Prepare a scratch directory
Tested on Linux x86_64 with GCC 14.2.0. Install a C++20-capable GCC compiler and its standard-library development files, with g++ on PATH. You also need Bash, a text editor, and mktemp, rm, and rmdir. No third-party library is required.
Keep the same terminal open. Stop if setup fails before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as scope-restoration-guard-demo.cpp:
#include <iostream>
#include <stdexcept>
class RestoreFlag {
public:
explicit RestoreFlag(bool& target) noexcept
: target_(target), previous_(target)
{
target_ = true;
}
~RestoreFlag() noexcept { target_ = previous_; }
RestoreFlag(const RestoreFlag&) = delete;
RestoreFlag& operator=(const RestoreFlag&) = delete;
private:
bool& target_;
bool previous_;
};
int main()
{
bool processing = false;
std::cout << std::boolalpha;
try {
RestoreFlag restore_processing(processing);
std::cout << "Inside: " << processing << '\n';
throw std::runtime_error("simulated failure");
} catch (const std::runtime_error&) {
std::cout << "After exception: " << processing << '\n';
}
}Compile and run it. The second command runs only after compilation succeeds:
g++ -std=c++20 -Wall -Wextra -Werror -pedantic scope-restoration-guard-demo.cpp -o scope-restoration-guard-demo &&
./scope-restoration-guard-demoObserved output:
Inside: true
After exception: falseRead the result
The guard captures the previous flag value before enabling it. Stack unwinding destroys the guard when the exception leaves the try block, restoring the original value before the catch block runs. Deleted copy operations prevent multiple guards accidentally representing the same restoration action.
Apply it carefully
The referenced flag must outlive the guard. The destructor only assigns a bool and cannot throw. A production resource guard should similarly release its resource without throwing during unwinding. Prefer standard ownership and locking classes for resources they already support. Destructors do not provide cleanup after every abnormal termination, and this flag is not a thread-synchronization mechanism.
The C++ working draft documents the relevant behavior. This example uses C++20 features, not newer additions shown elsewhere in the current draft.
Clean up
Remove only the source and executable created here, then remove the empty practice directory:
rm -- scope-restoration-guard-demo.cpp scope-restoration-guard-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.