C++ Optional in Depth
An optional is a box that holds either one value or nothing. A function can return an optional when a result may not exist.
A finder that may miss
The findScore function searches two names. It returns the score for a hit and std::nullopt for a miss, and value_or supplies the fallback.
#include <iostream>
#include <optional>
#include <string>
std::optional<int> findScore(const std::string& name) {
if (name == "Hana") {
return 90;
}
if (name == "Omar") {
return 75;
}
return std::nullopt;
}
int main() {
std::optional<int> hit = findScore("Hana");
std::optional<int> miss = findScore("Lena");
std::cout << std::boolalpha;
std::cout << hit.has_value() << " " << hit.value_or(0) << "\n";
std::cout << miss.has_value() << " " << miss.value_or(0) << "\n";
return 0;
}No result for a bad division
The divide function returns std::nullopt when the second number is zero. has_value chooses which line prints for each call.
#include <iostream>
#include <optional>
std::optional<int> divide(int top, int bottom) {
if (bottom == 0) {
return std::nullopt;
}
return top / bottom;
}
int main() {
std::optional<int> good = divide(20, 4);
std::optional<int> bad = divide(20, 0);
if (good.has_value()) {
std::cout << good.value() << "\n";
} else {
std::cout << "No result" << "\n";
}
if (bad.has_value()) {
std::cout << bad.value() << "\n";
} else {
std::cout << "No result" << "\n";
}
return 0;
}An optional field in a class
The Profile class holds an optional nickname. The field starts empty, emplace fills it, and reset empties it again.
#include <iostream>
#include <optional>
#include <string>
class Profile {
public:
std::optional<std::string> nickname;
};
int main() {
Profile profile;
std::cout << std::boolalpha;
std::cout << profile.nickname.has_value() << "\n";
profile.nickname.emplace("Ace");
std::cout << profile.nickname.has_value() << " " << profile.nickname.value() << "\n";
profile.nickname.reset();
std::cout << profile.nickname.has_value() << "\n";
return 0;
}Example
#include <iostream>
#include <optional>
#include <string>
#include <vector>
struct Setting {
std::string name;
int value;
};
std::optional<int> findSetting(const std::vector<Setting>& settings, const std::string& name) {
for (const Setting& setting : settings) {
if (setting.name == name) {
return setting.value;
}
}
return std::nullopt;
}
int main() {
std::vector<Setting> settings = {{"Volume", 7}, {"Brightness", 5}, {"Speed", 3}};
std::optional<int> volume = findSetting(settings, "Volume");
std::optional<int> color = findSetting(settings, "Color");
std::cout << std::boolalpha;
std::cout << "Volume found: " << volume.has_value() << ", value " << volume.value_or(0) << "\n";
std::cout << "Color found: " << color.has_value() << ", value " << color.value_or(0) << "\n";
return 0;
}Key points
- The <optional> header gives a program the std::optional type.
- An optional holds either one value or nothing, and that state is part of the type.
- has_value() tells whether the box currently holds a value.
- value_or gives the held value, or a fallback when the box is empty.
- value() gives the held value directly.
- std::nullopt is the empty value a function returns when there is no result.
- emplace puts a new value into an optional, and reset empties it.
In this example, findSetting walks a small table of settings and returns an optional for each lookup. The Volume lookup hits and prints true with the value 7. The Color lookup misses and prints false with the fallback 0.
Note
An optional stores its value inside itself, not behind a pointer.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A function returns std::nullopt. What does has_value() give for that result?