When writing C++ projects, do you often encounter scenarios like this: you write a function that may return a value or may not find a result, so you return <span>-1</span>, <span>nullptr</span>, or simply use a boolean value with an output parameter.
These “workarounds” can be used, but they always feel a bit awkward.
C++17 brings us a more elegant tool — <span>std::optional</span>, which helps you naturally express the semantics of “this value may exist or may not exist”.
Today, let’s talk about its usage and application scenarios. 🚀
🔧 Basic Concepts and Operations
<span>std::optional<T></span> can be seen as a “box” that either contains a value of type <span>T</span> or is empty.
Include the header file:
#include <optional>
✅ Creation and Assignment
std::optional<int> a; // empty
std::optional<int> b = 10; // has value
b = 20; // reassign
b.reset(); // clear
🔍 Check for Value:
if (result.has_value()) {
// has value
}
You can also write it directly:
if (result) {
// has value
}
🔓 Unpack Value:
std::optional<int> opt = 10;
int value = *opt; // direct unpacking, not recommended as it may throw an exception
int value = opt.value_or(0); // recommended to access with a default value, returns 0 if opt is empty
Application Scenarios 🎯
1. Representing a “Possible Failure” Return Value
The most common usage is to replace “special values” to indicate failure.
std::optional<int> FindIndex(const std::vector<int>& vec, int target) {
for (size_t i = 0; i < vec.size(); ++i) {
if (vec[i] == target) {
return static_cast<int>(i);
}
}
return std::nullopt; // not found
}
This way, the caller can clearly make a judgment:
auto idx = FindIndex({1,2,3}, 2);
if (idx) {
std::cout << "Found at " << *idx << "\n";
} else {
std::cout << "Not found\n";
}
Isn’t it clearer than returning <span>-1</span>? ✨
2. Replacing “bool + Output Parameter”
Many C-style APIs are designed this way:
bool GetResult(int& out_val);
When calling, you need to prepare a variable first, which will only be assigned if successful, making it look quite awkward.
Using <span>std::optional</span>, we can express it more naturally:
std::optional<int> GetResult() {
if (/*success*/) {
return 42;
}
return std::nullopt;
}
auto r = GetResult();
if (r) {
std::cout << "Result: " << *r << "\n";
} else {
std::cout << "No result" << "\n";
}
This way, the function becomes more concise, no longer requiring additional output parameters. 🌟
3. Representing “Optional Parameters” as Function Parameters
Sometimes when we write functions, we want a certain parameter to be optional. Previously, we might have used default values <span>""</span> or <span>-1</span> as a workaround.
Now, we can use a more intuitive syntax:
void PrintMessage(std::string msg, std::optional<std::string> prefix = std::nullopt) {
if (prefix) {
std::cout << *prefix << ": ";
}
std::cout << msg << "\n";
}
PrintMessage("Hello");
PrintMessage("World", "Debug");
You can either pass a value or not pass anything at all. 👐
Conclusion 🍵
<span>std::optional</span> is a small yet beautiful tool introduced in C++17, and its core function is:
👉 To make the semantics of “value exists / does not exist” clearer, avoiding the use of <span>-1</span>, <span>nullptr</span>, and other ambiguous placeholders.
Next time you write an interface, consider using <span>std::optional</span>, and I believe you will come to love it. 😎