You can use this to reliably release a semaphore to avoid dead-lock in the face of exceptions or early returns:

// ... do something that may throw or return early
sem.release();

If an early return is taken or an exception is thrown before the sem.release() call is reached, the semaphore is not released, possibly preventing the thread waiting in the corresponding sem.acquire() call from ever continuing execution.

When using RAII instead:

constQSemaphoreReleaser releaser(sem);
// ... do something that may throw or early return// implicitly calls sem.release() here and at every other return in between

this can no longer happen, because the compiler will make sure that the QSemaphoreReleaser destructor is always called, and therefore the semaphore is always released.

QSemaphoreReleaser is move-enabled and can therefore be returned from functions to transfer responsibility for releasing a semaphore out of a function or a scope:

QSemaphoreReleaser::~QSemaphoreReleaser()

Cancels this QSemaphoreReleaser such that the destructor will no longer call semaphore()->release(). Returns the value of semaphore() before this call. After this call, semaphore() will return nullptr.