I'd use a flagged optional type, e.g. Boost.Optional. While you can use NULL for pointers, you may as well use an optional<char *>
as well as an optional<int>
.
If you insist on using NULL, you may decide that some int values aren't legal inputs (e.g. (unsigned)-1); you could reject them if the user supplies them, and use them to represent "no option supplied".
The essence of an optional wrapper is: boolean flag for whether option is present, and wrapped data type, e.g.:
template <class T>
struct optional<T> {
T val;
bool none;
bool have() { return !none; }
optional() : none(true)
optional(T const& val) : val(val),none(false) {}
void operator=(T const& t) { none=false; val=t; }
// etc; make none and val private if you wish
};