In general, it's best practice to avoid side effects in assertions. Assertions should be used to check invariants and preconditions, not to modify state or perform logic that affects program behavior.
Here's an example of an assertion with a side effect:
#include <cassert>
void risky_increment(int& x) {
assert(++x > 0);
}
int main() {
int a = 0;
risky_increment(a);
// What's the value of a here?
}
The problem with this is:
x
 is being modified by the assertion.Instead, perform any necessary modifications separately and then assert on the result:
#include <cassert>
void safe_increment(int& x) {
++x;
assert(x > 0);
}
This makes the increment explicit and ensures the function behavior is consistent regardless of assertion settings.
A similar guideline applies to assertions in constructors or destructors. Avoid complex logic or side effects that could leave the object in an indeterminate state if an assertion fails:
class User {
std::string name;
int age;
public:
User(std::string name, int age) : name(name), age(age) {
assert(age >= 0);
}
};
Here, if the age assertion fails, the User
object will still be constructed with whatever name
value was provided. It's cleaner to validate the inputs before performing any initialization.
In summary:
Answers to questions are automatically generated and may not have been reviewed.
Learn how we can ensure that our application is in a valid state using compile-time and run-time assertions.