Специально для этого в C++ начиная с 17 версии стандарта завезли
std::optional
Эта штука позволяет хранить внутри себя значение или помнить, что значение в данный момент не задано.
Пример:
#include <optional>
using ochar = std::optional<char>;
ochar a = 'a';
ochar b;
if(a) { std::cout << " a exists and contains " << *a << std::endl;}
if(!b) {std::cout << " b does not exist" << std::endl;}
Пример из мануала:
#include <string>
#include <functional>
#include <iostream>
#include <optional>
// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b) {
if (b)
return "Godzilla";
return {};
}
// std::nullopt can be used to create any (empty) std::optional
auto create2(bool b) {
return b ? std::optional<std::string>{"Godzilla"} : std::nullopt;
}
// std::reference_wrapper may be used to return a reference
auto create_ref(bool b) {
static std::string value = "Godzilla";
return b ? std::optional<std::reference_wrapper<std::string>>{value}
: std::nullopt;
}
int main()
{
std::cout << "create(false) returned "
<< create(false).value_or("empty") << '\n';
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create2(true)) {
std::cout << "create2(true) returned " << *str << '\n';
}
if (auto str = create_ref(true)) {
// using get() to access the reference_wrapper's value
std::cout << "create_ref(true) returned " << str->get() << '\n';
str->get() = "Mothra";
std::cout << "modifying it changed it to " << str->get() << '\n';
}
}
Важное отличие от использования, например, указателя - optional не делает динамических выделений памяти, значение хранится (или не хранится) прямо у нее внутри.